Index: stable/12/usr.sbin/ctld/ctl.conf.5 =================================================================== --- stable/12/usr.sbin/ctld/ctl.conf.5 (revision 366626) +++ stable/12/usr.sbin/ctld/ctl.conf.5 (revision 366627) @@ -1,587 +1,595 @@ .\" Copyright (c) 2012 The FreeBSD Foundation .\" Copyright (c) 2015 Alexander Motin .\" 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 AUTHORS 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 AUTHORS 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$ .\" .Dd July 21, 2016 .Dt CTL.CONF 5 .Os .Sh NAME .Nm ctl.conf .Nd CAM Target Layer / iSCSI target daemon configuration file .Sh DESCRIPTION The .Nm configuration file is used by the .Xr ctld 8 daemon. Lines starting with .Ql # are interpreted as comments. The general syntax of the .Nm file is: .Bd -literal -offset indent .No pidfile Ar path .No auth-group Ar name No { .Dl chap Ar user Ar secret .Dl ... } .No portal-group Ar name No { .Dl listen Ar address .\".Dl listen-iser Ar address .Dl discovery-auth-group Ar name .Dl ... } .No target Ar name { .Dl auth-group Ar name .Dl portal-group Ar name .Dl lun Ar number No { .Dl path Ar path .Dl } .Dl ... } .Ed .Ss Global Context .Bl -tag -width indent .It Ic auth-group Ar name Create an .Sy auth-group configuration context, defining a new auth-group, which can then be assigned to any number of targets. .It Ic debug Ar level The debug verbosity level. The default is 0. .It Ic maxproc Ar number The limit for concurrently running child processes handling incoming connections. The default is 30. A setting of 0 disables the limit. .It Ic pidfile Ar path The path to the pidfile. The default is .Pa /var/run/ctld.pid . .It Ic portal-group Ar name Create a .Sy portal-group configuration context, defining a new portal-group, which can then be assigned to any number of targets. .It Ic lun Ar name Create a .Sy lun configuration context, defining a LUN to be exported by any number of targets. .It Ic target Ar name Create a .Sy target configuration context, which can optionally contain one or more .Sy lun contexts. .It Ic timeout Ar seconds The timeout for login sessions, after which the connection will be forcibly terminated. The default is 60. A setting of 0 disables the timeout. .It Ic isns-server Ar address An IPv4 or IPv6 address and optionally port of iSNS server to register on. .It Ic isns-period Ar seconds iSNS registration period. Registered Network Entity not updated during this period will be unregistered. The default is 900. .It Ic isns-timeout Ar seconds Timeout for iSNS requests. The default is 5. .El .Ss auth-group Context .Bl -tag -width indent .It Ic auth-type Ar type Sets the authentication type. Type can be either .Qq Ar none , .Qq Ar deny , .Qq Ar chap , or .Qq Ar chap-mutual . In most cases it is not necessary to set the type using this clause; it is usually used to disable authentication for a given .Sy auth-group . .It Ic chap Ar user Ar secret A set of CHAP authentication credentials. Note that for any .Sy auth-group , the configuration may only contain either .Sy chap or .Sy chap-mutual entries; it is an error to mix them. .It Ic chap-mutual Ar user Ar secret Ar mutualuser Ar mutualsecret A set of mutual CHAP authentication credentials. Note that for any .Sy auth-group , the configuration may only contain either .Sy chap or .Sy chap-mutual entries; it is an error to mix them. .It Ic initiator-name Ar initiator-name An iSCSI initiator name. Only initiators with a name matching one of the defined names will be allowed to connect. If not defined, there will be no restrictions based on initiator name. .It Ic initiator-portal Ar address Ns Op / Ns Ar prefixlen An iSCSI initiator portal: an IPv4 or IPv6 address, optionally followed by a literal slash and a prefix length. Only initiators with an address matching one of the defined addresses will be allowed to connect. If not defined, there will be no restrictions based on initiator address. .El .Ss portal-group Context .Bl -tag -width indent .It Ic discovery-auth-group Ar name Assign a previously defined authentication group to the portal group, to be used for target discovery. By default, portal groups are assigned predefined .Sy auth-group .Qq Ar default , which denies discovery. Another predefined .Sy auth-group , .Qq Ar no-authentication , may be used to permit discovery without authentication. .It Ic discovery-filter Ar filter Determines which targets are returned during discovery. Filter can be either .Qq Ar none , .Qq Ar portal , .Qq Ar portal-name , or .Qq Ar portal-name-auth . When set to .Qq Ar none , discovery will return all targets assigned to that portal group. When set to .Qq Ar portal , discovery will not return targets that cannot be accessed by the initiator because of their .Sy initiator-portal . When set to .Qq Ar portal-name , the check will include both .Sy initiator-portal and .Sy initiator-name . When set to .Qq Ar portal-name-auth , the check will include .Sy initiator-portal , .Sy initiator-name , and authentication credentials. The target is returned if it does not require CHAP authentication, or if the CHAP user and secret used during discovery match those used by the target. Note that when using .Qq Ar portal-name-auth , targets that require CHAP authentication will only be returned if .Sy discovery-auth-group requires CHAP. The default is .Qq Ar none . .It Ic listen Ar address 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 offload Ar driver Define iSCSI hardware offload driver to use for this .Sy portal-group . The default is .Qq Ar none . .It Ic option Ar name Ar value The CTL-specific port options passed to the kernel. .It Ic redirect 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. .It Ic tag Ar value Unique 16-bit tag value of this .Sy portal-group . If not specified, the value is generated automatically. .It Ic foreign Specifies that this .Sy portal-group is listened by some other host. This host will announce it on discovery stage, but won't listen. +.It Ic dscp Ar value +The DiffServ Codepoint used for sending data. The DSCP can be +set to numeric, or hexadecimal values directly, as well as the +well-defined +.Qq Ar CSx +and +.Qq Ar AFxx +codepoints. .El .Ss target Context .Bl -tag -width indent .It Ic alias Ar text Assign a human-readable description to the target. There is no default. .It Ic auth-group Ar name Assign a previously defined authentication group to the target. By default, targets that do not specify their own auth settings, using clauses such as .Sy chap or .Sy initiator-name , are assigned predefined .Sy auth-group .Qq Ar default , which denies all access. Another predefined .Sy auth-group , .Qq Ar no-authentication , may be used to permit access without authentication. Note that this clause can be overridden using the second argument to a .Sy portal-group clause. .It Ic auth-type Ar type Sets the authentication type. Type can be either .Qq Ar none , .Qq Ar deny , .Qq Ar chap , or .Qq Ar chap-mutual . In most cases it is not necessary to set the type using this clause; it is usually used to disable authentication for a given .Sy target . This clause is mutually exclusive with .Sy auth-group ; one cannot use both in a single target. .It Ic chap Ar user Ar secret A set of CHAP authentication credentials. Note that targets must only use one of .Sy auth-group , chap , No or Sy chap-mutual ; it is a configuration error to mix multiple types in one target. .It Ic chap-mutual Ar user Ar secret Ar mutualuser Ar mutualsecret A set of mutual CHAP authentication credentials. Note that targets must only use one of .Sy auth-group , chap , No or Sy chap-mutual ; it is a configuration error to mix multiple types in one target. .It Ic initiator-name Ar initiator-name An iSCSI initiator name. Only initiators with a name matching one of the defined names will be allowed to connect. If not defined, there will be no restrictions based on initiator name. This clause is mutually exclusive with .Sy auth-group ; one cannot use both in a single target. .It Ic initiator-portal Ar address Ns Op / Ns Ar prefixlen An iSCSI initiator portal: an IPv4 or IPv6 address, optionally followed by a literal slash and a prefix length. Only initiators with an address matching one of the defined addresses will be allowed to connect. If not defined, there will be no restrictions based on initiator address. This clause is mutually exclusive with .Sy auth-group ; one cannot use both in a single target. .Pp The .Sy auth-type , .Sy chap , .Sy chap-mutual , .Sy initiator-name , and .Sy initiator-portal clauses in the target context provide an alternative to assigning an .Sy auth-group defined separately, useful in the common case of authentication settings specific to a single target. .It Ic portal-group Ar name Op Ar ag-name Assign a previously defined portal group to the target. The default portal group is .Qq Ar default , which makes the target available on TCP port 3260 on all configured IPv4 and IPv6 addresses. Optional second argument specifies .Sy auth-group for connections to this specific portal group. If second argument is not specified, target .Sy auth-group is used. .It Ic port Ar name .It Ic port Ar name/pp .It Ic port Ar name/pp/vp Assign specified CTL port (such as "isp0" or "isp2/1") to the target. This is used to export the target through a specific physical - eg Fibre Channel - port, in addition to portal-groups configured for the target. Use .Cm "ctladm portlist" command to retrieve the list of available ports. On startup .Xr ctld 8 configures LUN mapping and enables all assigned ports. Each port can be assigned to only one target. .It Ic redirect 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 Ar name Export previously defined .Sy lun by the parent target. .It Ic lun Ar number Create a .Sy lun configuration context, defining a LUN exported by the parent target. .Pp This is an alternative to defining the LUN separately, useful in the common case of a LUN being exported by a single target. .El .Ss lun Context .Bl -tag -width indent .It Ic backend Ar block No | Ar ramdisk The CTL backend to use for a given LUN. Valid choices are .Qq Ar block and .Qq Ar ramdisk ; block is used for LUNs backed by files or disk device nodes; ramdisk is a bitsink device, used mostly for testing. The default backend is block. .It Ic blocksize Ar size The blocksize visible to the initiator. The default blocksize is 512 for disks, and 2048 for CD/DVDs. .It Ic ctl-lun Ar lun_id Global numeric identifier to use for a given LUN inside CTL. By default CTL allocates those IDs dynamically, but explicit specification may be needed for consistency in HA configurations. .It Ic device-id Ar string The SCSI Device Identification string presented to the initiator. .It Ic device-type Ar type Specify the SCSI device type to use when creating the LUN. Currently CTL supports Direct Access (type 0), Processor (type 3) and CD/DVD (type 5) LUNs. .It Ic option Ar name Ar value The CTL-specific options passed to the kernel. All CTL-specific options are documented in the .Sx OPTIONS section of .Xr ctladm 8 . .It Ic path Ar path The path to the file, device node, or .Xr zfs 8 volume used to back the LUN. For optimal performance, create the volume with the .Qq Ar volmode=dev property set. .It Ic serial Ar string The SCSI serial number presented to the initiator. .It Ic size Ar size The LUN size, in bytes. .El .Sh FILES .Bl -tag -width ".Pa /etc/ctl.conf" -compact .It Pa /etc/ctl.conf The default location of the .Xr ctld 8 configuration file. .El .Sh EXAMPLES .Bd -literal auth-group ag0 { chap-mutual "user" "secret" "mutualuser" "mutualsecret" chap-mutual "user2" "secret2" "mutualuser" "mutualsecret" initiator-portal 192.168.1.1/16 } auth-group ag1 { auth-type none initiator-name "iqn.2012-06.com.example:initiatorhost1" initiator-name "iqn.2012-06.com.example:initiatorhost2" initiator-portal 192.168.1.1/24 initiator-portal [2001:db8::de:ef] } portal-group pg0 { discovery-auth-group no-authentication listen 0.0.0.0:3260 listen [::]:3260 listen [fe80::be:ef]:3261 } target iqn.2012-06.com.example:target0 { alias "Example target" auth-group no-authentication lun 0 { path /dev/zvol/tank/example_0 blocksize 4096 size 4G } } lun example_1 { path /dev/zvol/tank/example_1 option naa 0x50015178f369f093 } target iqn.2012-06.com.example:target1 { auth-group ag0 portal-group pg0 lun 0 example_1 lun 1 { path /dev/zvol/tank/example_2 option vendor "FreeBSD" } } target naa.50015178f369f092 { port isp0 port isp1 lun 0 example_1 } .Ed .Pp An equivalent configuration in UCL format, for use with .Fl u : .Bd -literal auth-group { ag0 { chap-mutual = [ { user = "user" secret = "secretsecret" mutual-user = "mutualuser" mutual-secret = "mutualsecret" }, { user = "user2" secret = "secret2secret2" mutual-user = "mutualuser" mutual-secret = "mutualsecret" } ] } ag1 { auth-type = none initiator-name = [ "iqn.2012-06.com.example:initiatorhost1", "iqn.2012-06.com.example:initiatorhost2" ] initiator-portal = [192.168.1.1/24, "[2001:db8::de:ef]"] } } portal-group { pg0 { discovery-auth-group = no-authentication listen = [ 0.0.0.0:3260, "[::]:3260", "[fe80::be:ef]:3261" ] } } lun { example_0 { path = /dev/zvol/tank/example_0 blocksize = 4096 size = "4G" } example_1 { path = /dev/zvol/tank/example_1 options { naa = "0x50015178f369f093" } } example_2 { path = /dev/zvol/tank/example_2 options { vendor = "FreeBSD" } } } target { "iqn.2012-06.com.example:target0" { alias = "Example target" auth-group = no-authentication lun = [ { number = 0, name = example_0 }, ] } "iqn.2012-06.com.example:target1" { auth-group = ag0 portal-group { name = pg0 } lun = [ { number = 0, name = example_1 }, { number = 1, name = example_2 } ] } naa.50015178f369f092 { port = isp0 lun = [ { number = 0, name = example_1 } ] } } .Ed .Sh SEE ALSO .Xr ctl 4 , .Xr ctladm 8 , .Xr ctld 8 , .Xr zfs 8 .Sh AUTHORS The .Nm configuration file functionality for .Xr ctld 8 was developed by .An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org under sponsorship from the FreeBSD Foundation. Index: stable/12/usr.sbin/ctld/ctld.c =================================================================== --- stable/12/usr.sbin/ctld/ctld.c (revision 366626) +++ stable/12/usr.sbin/ctld/ctld.c (revision 366627) @@ -1,2790 +1,2817 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctld.h" #include "isns.h" bool proxy_mode = false; static volatile bool sighup_received = false; static volatile bool sigterm_received = false; static volatile bool sigalrm_received = false; static int nchildren = 0; static uint16_t last_portal_group_tag = 0xff; static void usage(void) { fprintf(stderr, "usage: ctld [-d][-u][-f config-file]\n"); exit(1); } char * checked_strdup(const char *s) { char *c; c = strdup(s); if (c == NULL) log_err(1, "strdup"); return (c); } struct conf * conf_new(void) { struct conf *conf; conf = calloc(1, sizeof(*conf)); if (conf == NULL) log_err(1, "calloc"); TAILQ_INIT(&conf->conf_luns); TAILQ_INIT(&conf->conf_targets); TAILQ_INIT(&conf->conf_auth_groups); TAILQ_INIT(&conf->conf_ports); TAILQ_INIT(&conf->conf_portal_groups); TAILQ_INIT(&conf->conf_pports); TAILQ_INIT(&conf->conf_isns); conf->conf_isns_period = 900; conf->conf_isns_timeout = 5; conf->conf_debug = 0; conf->conf_timeout = 60; conf->conf_maxproc = 30; return (conf); } void conf_delete(struct conf *conf) { struct lun *lun, *ltmp; struct target *targ, *tmp; struct auth_group *ag, *cagtmp; struct portal_group *pg, *cpgtmp; struct pport *pp, *pptmp; struct isns *is, *istmp; assert(conf->conf_pidfh == NULL); TAILQ_FOREACH_SAFE(lun, &conf->conf_luns, l_next, ltmp) lun_delete(lun); TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp) target_delete(targ); TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp) auth_group_delete(ag); TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp) portal_group_delete(pg); TAILQ_FOREACH_SAFE(pp, &conf->conf_pports, pp_next, pptmp) pport_delete(pp); TAILQ_FOREACH_SAFE(is, &conf->conf_isns, i_next, istmp) isns_delete(is); assert(TAILQ_EMPTY(&conf->conf_ports)); free(conf->conf_pidfile_path); free(conf); } static struct auth * auth_new(struct auth_group *ag) { struct auth *auth; auth = calloc(1, sizeof(*auth)); if (auth == NULL) log_err(1, "calloc"); auth->a_auth_group = ag; TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next); return (auth); } static void auth_delete(struct auth *auth) { TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next); free(auth->a_user); free(auth->a_secret); free(auth->a_mutual_user); free(auth->a_mutual_secret); free(auth); } const struct auth * auth_find(const struct auth_group *ag, const char *user) { const struct auth *auth; TAILQ_FOREACH(auth, &ag->ag_auths, a_next) { if (strcmp(auth->a_user, user) == 0) return (auth); } return (NULL); } static void auth_check_secret_length(struct auth *auth) { size_t len; len = strlen(auth->a_secret); if (len > 16) { if (auth->a_auth_group->ag_name != NULL) log_warnx("secret for user \"%s\", auth-group \"%s\", " "is too long; it should be at most 16 characters " "long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("secret for user \"%s\", target \"%s\", " "is too long; it should be at most 16 characters " "long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (len < 12) { if (auth->a_auth_group->ag_name != NULL) log_warnx("secret for user \"%s\", auth-group \"%s\", " "is too short; it should be at least 12 characters " "long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("secret for user \"%s\", target \"%s\", " "is too short; it should be at least 12 characters " "long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (auth->a_mutual_secret != NULL) { len = strlen(auth->a_mutual_secret); if (len > 16) { if (auth->a_auth_group->ag_name != NULL) log_warnx("mutual secret for user \"%s\", " "auth-group \"%s\", is too long; it should " "be at most 16 characters long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("mutual secret for user \"%s\", " "target \"%s\", is too long; it should " "be at most 16 characters long", auth->a_user, auth->a_auth_group->ag_target->t_name); } if (len < 12) { if (auth->a_auth_group->ag_name != NULL) log_warnx("mutual secret for user \"%s\", " "auth-group \"%s\", is too short; it " "should be at least 12 characters long", auth->a_user, auth->a_auth_group->ag_name); else log_warnx("mutual secret for user \"%s\", " "target \"%s\", is too short; it should be " "at least 12 characters long", auth->a_user, auth->a_auth_group->ag_target->t_name); } } } const struct auth * auth_new_chap(struct auth_group *ag, const char *user, const char *secret) { struct auth *auth; if (ag->ag_type == AG_TYPE_UNKNOWN) ag->ag_type = AG_TYPE_CHAP; if (ag->ag_type != AG_TYPE_CHAP) { if (ag->ag_name != NULL) log_warnx("cannot mix \"chap\" authentication with " "other types for auth-group \"%s\"", ag->ag_name); else log_warnx("cannot mix \"chap\" authentication with " "other types for target \"%s\"", ag->ag_target->t_name); return (NULL); } auth = auth_new(ag); auth->a_user = checked_strdup(user); auth->a_secret = checked_strdup(secret); auth_check_secret_length(auth); return (auth); } const struct auth * auth_new_chap_mutual(struct auth_group *ag, const char *user, const char *secret, const char *user2, const char *secret2) { struct auth *auth; if (ag->ag_type == AG_TYPE_UNKNOWN) ag->ag_type = AG_TYPE_CHAP_MUTUAL; if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) { if (ag->ag_name != NULL) log_warnx("cannot mix \"chap-mutual\" authentication " "with other types for auth-group \"%s\"", ag->ag_name); else log_warnx("cannot mix \"chap-mutual\" authentication " "with other types for target \"%s\"", ag->ag_target->t_name); return (NULL); } auth = auth_new(ag); auth->a_user = checked_strdup(user); auth->a_secret = checked_strdup(secret); auth->a_mutual_user = checked_strdup(user2); auth->a_mutual_secret = checked_strdup(secret2); auth_check_secret_length(auth); return (auth); } const struct auth_name * auth_name_new(struct auth_group *ag, const char *name) { struct auth_name *an; an = calloc(1, sizeof(*an)); if (an == NULL) log_err(1, "calloc"); an->an_auth_group = ag; an->an_initator_name = checked_strdup(name); TAILQ_INSERT_TAIL(&ag->ag_names, an, an_next); return (an); } static void auth_name_delete(struct auth_name *an) { TAILQ_REMOVE(&an->an_auth_group->ag_names, an, an_next); free(an->an_initator_name); free(an); } bool auth_name_defined(const struct auth_group *ag) { if (TAILQ_EMPTY(&ag->ag_names)) return (false); return (true); } const struct auth_name * auth_name_find(const struct auth_group *ag, const char *name) { const struct auth_name *auth_name; TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) { if (strcmp(auth_name->an_initator_name, name) == 0) return (auth_name); } return (NULL); } int auth_name_check(const struct auth_group *ag, const char *initiator_name) { if (!auth_name_defined(ag)) return (0); if (auth_name_find(ag, initiator_name) == NULL) return (1); return (0); } const struct auth_portal * auth_portal_new(struct auth_group *ag, const char *portal) { struct auth_portal *ap; char *net, *mask, *str, *tmp; int len, dm, m; ap = calloc(1, sizeof(*ap)); if (ap == NULL) log_err(1, "calloc"); ap->ap_auth_group = ag; ap->ap_initator_portal = checked_strdup(portal); mask = str = checked_strdup(portal); net = strsep(&mask, "/"); if (net[0] == '[') net++; len = strlen(net); if (len == 0) goto error; if (net[len - 1] == ']') net[len - 1] = 0; if (strchr(net, ':') != NULL) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ap->ap_sa; sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; if (inet_pton(AF_INET6, net, &sin6->sin6_addr) <= 0) goto error; dm = 128; } else { struct sockaddr_in *sin = (struct sockaddr_in *)&ap->ap_sa; sin->sin_len = sizeof(*sin); sin->sin_family = AF_INET; if (inet_pton(AF_INET, net, &sin->sin_addr) <= 0) goto error; dm = 32; } if (mask != NULL) { m = strtol(mask, &tmp, 0); if (m < 0 || m > dm || tmp[0] != 0) goto error; } else m = dm; ap->ap_mask = m; free(str); TAILQ_INSERT_TAIL(&ag->ag_portals, ap, ap_next); return (ap); error: free(str); free(ap); log_warnx("incorrect initiator portal \"%s\"", portal); return (NULL); } static void auth_portal_delete(struct auth_portal *ap) { TAILQ_REMOVE(&ap->ap_auth_group->ag_portals, ap, ap_next); free(ap->ap_initator_portal); free(ap); } bool auth_portal_defined(const struct auth_group *ag) { if (TAILQ_EMPTY(&ag->ag_portals)) return (false); return (true); } const struct auth_portal * auth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *ss) { const struct auth_portal *ap; const uint8_t *a, *b; int i; uint8_t bmask; TAILQ_FOREACH(ap, &ag->ag_portals, ap_next) { if (ap->ap_sa.ss_family != ss->ss_family) continue; if (ss->ss_family == AF_INET) { a = (const uint8_t *) &((const struct sockaddr_in *)ss)->sin_addr; b = (const uint8_t *) &((const struct sockaddr_in *)&ap->ap_sa)->sin_addr; } else { a = (const uint8_t *) &((const struct sockaddr_in6 *)ss)->sin6_addr; b = (const uint8_t *) &((const struct sockaddr_in6 *)&ap->ap_sa)->sin6_addr; } for (i = 0; i < ap->ap_mask / 8; i++) { if (a[i] != b[i]) goto next; } if (ap->ap_mask % 8) { bmask = 0xff << (8 - (ap->ap_mask % 8)); if ((a[i] & bmask) != (b[i] & bmask)) goto next; } return (ap); next: ; } return (NULL); } int auth_portal_check(const struct auth_group *ag, const struct sockaddr_storage *sa) { if (!auth_portal_defined(ag)) return (0); if (auth_portal_find(ag, sa) == NULL) return (1); return (0); } struct auth_group * auth_group_new(struct conf *conf, const char *name) { struct auth_group *ag; if (name != NULL) { ag = auth_group_find(conf, name); if (ag != NULL) { log_warnx("duplicated auth-group \"%s\"", name); return (NULL); } } ag = calloc(1, sizeof(*ag)); if (ag == NULL) log_err(1, "calloc"); if (name != NULL) ag->ag_name = checked_strdup(name); TAILQ_INIT(&ag->ag_auths); TAILQ_INIT(&ag->ag_names); TAILQ_INIT(&ag->ag_portals); ag->ag_conf = conf; TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next); return (ag); } void auth_group_delete(struct auth_group *ag) { struct auth *auth, *auth_tmp; struct auth_name *auth_name, *auth_name_tmp; struct auth_portal *auth_portal, *auth_portal_tmp; TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, auth_tmp) auth_delete(auth); TAILQ_FOREACH_SAFE(auth_name, &ag->ag_names, an_next, auth_name_tmp) auth_name_delete(auth_name); TAILQ_FOREACH_SAFE(auth_portal, &ag->ag_portals, ap_next, auth_portal_tmp) auth_portal_delete(auth_portal); free(ag->ag_name); free(ag); } struct auth_group * auth_group_find(const struct conf *conf, const char *name) { struct auth_group *ag; TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0) return (ag); } return (NULL); } int auth_group_set_type(struct auth_group *ag, const char *str) { int type; if (strcmp(str, "none") == 0) { type = AG_TYPE_NO_AUTHENTICATION; } else if (strcmp(str, "deny") == 0) { type = AG_TYPE_DENY; } else if (strcmp(str, "chap") == 0) { type = AG_TYPE_CHAP; } else if (strcmp(str, "chap-mutual") == 0) { type = AG_TYPE_CHAP_MUTUAL; } else { if (ag->ag_name != NULL) log_warnx("invalid auth-type \"%s\" for auth-group " "\"%s\"", str, ag->ag_name); else log_warnx("invalid auth-type \"%s\" for target " "\"%s\"", str, ag->ag_target->t_name); return (1); } if (ag->ag_type != AG_TYPE_UNKNOWN && ag->ag_type != type) { if (ag->ag_name != NULL) { log_warnx("cannot set auth-type to \"%s\" for " "auth-group \"%s\"; already has a different " "type", str, ag->ag_name); } else { log_warnx("cannot set auth-type to \"%s\" for target " "\"%s\"; already has a different type", str, ag->ag_target->t_name); } return (1); } ag->ag_type = type; return (0); } static struct portal * portal_new(struct portal_group *pg) { struct portal *portal; portal = calloc(1, sizeof(*portal)); if (portal == NULL) log_err(1, "calloc"); TAILQ_INIT(&portal->p_targets); portal->p_portal_group = pg; TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next); return (portal); } static void portal_delete(struct portal *portal) { TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next); if (portal->p_ai != NULL) freeaddrinfo(portal->p_ai); free(portal->p_listen); free(portal); } struct portal_group * portal_group_new(struct conf *conf, const char *name) { struct portal_group *pg; pg = portal_group_find(conf, name); if (pg != NULL) { log_warnx("duplicated portal-group \"%s\"", name); return (NULL); } pg = calloc(1, sizeof(*pg)); if (pg == NULL) log_err(1, "calloc"); pg->pg_name = checked_strdup(name); TAILQ_INIT(&pg->pg_options); TAILQ_INIT(&pg->pg_portals); TAILQ_INIT(&pg->pg_ports); pg->pg_conf = conf; pg->pg_tag = 0; /* Assigned later in conf_apply(). */ + pg->pg_dscp = -1; TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next); return (pg); } void portal_group_delete(struct portal_group *pg) { struct portal *portal, *tmp; struct port *port, *tport; struct option *o, *otmp; TAILQ_FOREACH_SAFE(port, &pg->pg_ports, p_pgs, tport) port_delete(port); TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next); TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp) portal_delete(portal); TAILQ_FOREACH_SAFE(o, &pg->pg_options, o_next, otmp) option_delete(&pg->pg_options, o); free(pg->pg_name); free(pg->pg_offload); free(pg->pg_redirection); free(pg); } struct portal_group * portal_group_find(const struct conf *conf, const char *name) { struct portal_group *pg; TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (strcmp(pg->pg_name, name) == 0) return (pg); } return (NULL); } static int parse_addr_port(char *arg, const char *def_port, struct addrinfo **ai) { struct addrinfo hints; char *str, *addr, *ch; const char *port; int error, colons = 0; str = arg = strdup(arg); if (arg[0] == '[') { /* * IPv6 address in square brackets, perhaps with port. */ arg++; addr = strsep(&arg, "]"); if (arg == NULL) { free(str); return (1); } if (arg[0] == '\0') { port = def_port; } else if (arg[0] == ':') { port = arg + 1; } else { free(str); return (1); } } else { /* * Either IPv6 address without brackets - and without * a port - or IPv4 address. Just count the colons. */ for (ch = arg; *ch != '\0'; ch++) { if (*ch == ':') colons++; } if (colons > 1) { addr = arg; port = def_port; } else { addr = strsep(&arg, ":"); if (arg == NULL) port = def_port; else port = arg; } } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(addr, port, &hints, ai); free(str); return ((error != 0) ? 1 : 0); } int portal_group_add_listen(struct portal_group *pg, const char *value, bool iser) { struct portal *portal; portal = portal_new(pg); portal->p_listen = checked_strdup(value); portal->p_iser = iser; if (parse_addr_port(portal->p_listen, "3260", &portal->p_ai)) { log_warnx("invalid listen address %s", portal->p_listen); portal_delete(portal); return (1); } /* * XXX: getaddrinfo(3) may return multiple addresses; we should turn * those into multiple portals. */ return (0); } int isns_new(struct conf *conf, const char *addr) { struct isns *isns; isns = calloc(1, sizeof(*isns)); if (isns == NULL) log_err(1, "calloc"); isns->i_conf = conf; TAILQ_INSERT_TAIL(&conf->conf_isns, isns, i_next); isns->i_addr = checked_strdup(addr); if (parse_addr_port(isns->i_addr, "3205", &isns->i_ai)) { log_warnx("invalid iSNS address %s", isns->i_addr); isns_delete(isns); return (1); } /* * XXX: getaddrinfo(3) may return multiple addresses; we should turn * those into multiple servers. */ return (0); } void isns_delete(struct isns *isns) { TAILQ_REMOVE(&isns->i_conf->conf_isns, isns, i_next); free(isns->i_addr); if (isns->i_ai != NULL) freeaddrinfo(isns->i_ai); free(isns); } static int isns_do_connect(struct isns *isns) { int s; s = socket(isns->i_ai->ai_family, isns->i_ai->ai_socktype, isns->i_ai->ai_protocol); if (s < 0) { log_warn("socket(2) failed for %s", isns->i_addr); return (-1); } if (connect(s, isns->i_ai->ai_addr, isns->i_ai->ai_addrlen)) { log_warn("connect(2) failed for %s", isns->i_addr); close(s); return (-1); } return(s); } static int isns_do_register(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct target *target; struct portal *portal; struct portal_group *pg; struct port *port; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVATTRREG, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_delim(req); isns_req_add_str(req, 1, hostname); isns_req_add_32(req, 2, 2); /* 2 -- iSCSI */ isns_req_add_32(req, 6, conf->conf_isns_period); TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (pg->pg_unassigned) continue; TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { isns_req_add_addr(req, 16, portal->p_ai); isns_req_add_port(req, 17, portal->p_ai); } } TAILQ_FOREACH(target, &conf->conf_targets, t_next) { isns_req_add_str(req, 32, target->t_name); isns_req_add_32(req, 33, 1); /* 1 -- Target*/ if (target->t_alias != NULL) isns_req_add_str(req, 34, target->t_alias); TAILQ_FOREACH(port, &target->t_ports, p_ts) { if ((pg = port->p_portal_group) == NULL) continue; isns_req_add_32(req, 51, pg->pg_tag); TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { isns_req_add_addr(req, 49, portal->p_ai); isns_req_add_port(req, 50, portal->p_ai); } } } res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS register error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } static int isns_do_check(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVATTRQRY, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_str(req, 1, hostname); isns_req_add_delim(req); isns_req_add(req, 2, 0, NULL); res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS check error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } static int isns_do_deregister(struct isns *isns, int s, const char *hostname) { struct conf *conf = isns->i_conf; struct isns_req *req; int res = 0; uint32_t error; req = isns_req_create(ISNS_FUNC_DEVDEREG, ISNS_FLAG_CLIENT); isns_req_add_str(req, 32, TAILQ_FIRST(&conf->conf_targets)->t_name); isns_req_add_delim(req); isns_req_add_str(req, 1, hostname); res = isns_req_send(s, req); if (res < 0) { log_warn("send(2) failed for %s", isns->i_addr); goto quit; } res = isns_req_receive(s, req); if (res < 0) { log_warn("receive(2) failed for %s", isns->i_addr); goto quit; } error = isns_req_get_status(req); if (error != 0) { log_warnx("iSNS deregister error %d for %s", error, isns->i_addr); res = -1; } quit: isns_req_free(req); return (res); } void isns_register(struct isns *isns, struct isns *oldisns) { struct conf *conf = isns->i_conf; int s; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) { set_timeout(0, false); return; } gethostname(hostname, sizeof(hostname)); if (oldisns == NULL || TAILQ_EMPTY(&oldisns->i_conf->conf_targets)) oldisns = isns; isns_do_deregister(oldisns, s, hostname); isns_do_register(isns, s, hostname); close(s); set_timeout(0, false); } void isns_check(struct isns *isns) { struct conf *conf = isns->i_conf; int s, res; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) { set_timeout(0, false); return; } gethostname(hostname, sizeof(hostname)); res = isns_do_check(isns, s, hostname); if (res < 0) { isns_do_deregister(isns, s, hostname); isns_do_register(isns, s, hostname); } close(s); set_timeout(0, false); } void isns_deregister(struct isns *isns) { struct conf *conf = isns->i_conf; int s; char hostname[256]; if (TAILQ_EMPTY(&conf->conf_targets) || TAILQ_EMPTY(&conf->conf_portal_groups)) return; set_timeout(conf->conf_isns_timeout, false); s = isns_do_connect(isns); if (s < 0) return; gethostname(hostname, sizeof(hostname)); isns_do_deregister(isns, s, hostname); close(s); set_timeout(0, false); } int portal_group_set_filter(struct portal_group *pg, const char *str) { int filter; if (strcmp(str, "none") == 0) { filter = PG_FILTER_NONE; } else if (strcmp(str, "portal") == 0) { filter = PG_FILTER_PORTAL; } else if (strcmp(str, "portal-name") == 0) { filter = PG_FILTER_PORTAL_NAME; } else if (strcmp(str, "portal-name-auth") == 0) { filter = PG_FILTER_PORTAL_NAME_AUTH; } else { log_warnx("invalid discovery-filter \"%s\" for portal-group " "\"%s\"; valid values are \"none\", \"portal\", " "\"portal-name\", and \"portal-name-auth\"", str, pg->pg_name); return (1); } if (pg->pg_discovery_filter != PG_FILTER_UNKNOWN && pg->pg_discovery_filter != filter) { log_warnx("cannot set discovery-filter to \"%s\" for " "portal-group \"%s\"; already has a different " "value", str, pg->pg_name); return (1); } pg->pg_discovery_filter = filter; return (0); } int portal_group_set_offload(struct portal_group *pg, const char *offload) { if (pg->pg_offload != NULL) { log_warnx("cannot set offload to \"%s\" for " "portal-group \"%s\"; already defined", offload, pg->pg_name); return (1); } pg->pg_offload = checked_strdup(offload); 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) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': return (true); default: return (false); } } bool valid_iscsi_name(const char *name) { int i; if (strlen(name) >= MAX_NAME_LEN) { log_warnx("overlong name for target \"%s\"; max length allowed " "by iSCSI specification is %d characters", name, MAX_NAME_LEN); return (false); } /* * In the cases below, we don't return an error, just in case the admin * was right, and we're wrong. */ if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { for (i = strlen("iqn."); name[i] != '\0'; i++) { /* * XXX: We should verify UTF-8 normalisation, as defined * by 3.2.6.2: iSCSI Name Encoding. */ if (isalnum(name[i])) continue; if (name[i] == '-' || name[i] == '.' || name[i] == ':') continue; log_warnx("invalid character \"%c\" in target name " "\"%s\"; allowed characters are letters, digits, " "'-', '.', and ':'", name[i], name); break; } /* * XXX: Check more stuff: valid date and a valid reversed domain. */ } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { if (strlen(name) != strlen("eui.") + 16) log_warnx("invalid target name \"%s\"; the \"eui.\" " "should be followed by exactly 16 hexadecimal " "digits", name); for (i = strlen("eui."); name[i] != '\0'; i++) { if (!valid_hex(name[i])) { log_warnx("invalid character \"%c\" in target " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { if (strlen(name) > strlen("naa.") + 32) log_warnx("invalid target name \"%s\"; the \"naa.\" " "should be followed by at most 32 hexadecimal " "digits", name); for (i = strlen("naa."); name[i] != '\0'; i++) { if (!valid_hex(name[i])) { log_warnx("invalid character \"%c\" in target " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else { log_warnx("invalid target name \"%s\"; should start with " "either \"iqn.\", \"eui.\", or \"naa.\"", name); } return (true); } struct pport * pport_new(struct conf *conf, const char *name, uint32_t ctl_port) { struct pport *pp; pp = calloc(1, sizeof(*pp)); if (pp == NULL) log_err(1, "calloc"); pp->pp_conf = conf; pp->pp_name = checked_strdup(name); pp->pp_ctl_port = ctl_port; TAILQ_INIT(&pp->pp_ports); TAILQ_INSERT_TAIL(&conf->conf_pports, pp, pp_next); return (pp); } struct pport * pport_find(const struct conf *conf, const char *name) { struct pport *pp; TAILQ_FOREACH(pp, &conf->conf_pports, pp_next) { if (strcasecmp(pp->pp_name, name) == 0) return (pp); } return (NULL); } struct pport * pport_copy(struct pport *pp, struct conf *conf) { struct pport *ppnew; ppnew = pport_new(conf, pp->pp_name, pp->pp_ctl_port); return (ppnew); } void pport_delete(struct pport *pp) { struct port *port, *tport; TAILQ_FOREACH_SAFE(port, &pp->pp_ports, p_ts, tport) port_delete(port); TAILQ_REMOVE(&pp->pp_conf->conf_pports, pp, pp_next); free(pp->pp_name); free(pp); } struct port * port_new(struct conf *conf, struct target *target, struct portal_group *pg) { struct port *port; char *name; int ret; ret = asprintf(&name, "%s-%s", pg->pg_name, target->t_name); if (ret <= 0) log_err(1, "asprintf"); if (port_find(conf, name) != NULL) { log_warnx("duplicate port \"%s\"", name); free(name); return (NULL); } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); port->p_conf = conf; port->p_name = name; port->p_ioctl_port = 0; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; TAILQ_INSERT_TAIL(&pg->pg_ports, port, p_pgs); port->p_portal_group = pg; return (port); } struct port * port_new_ioctl(struct conf *conf, struct target *target, int pp, int vp) { struct pport *pport; struct port *port; char *pname; char *name; int ret; ret = asprintf(&pname, "ioctl/%d/%d", pp, vp); if (ret <= 0) { log_err(1, "asprintf"); return (NULL); } pport = pport_find(conf, pname); if (pport != NULL) { free(pname); return (port_new_pp(conf, target, pport)); } ret = asprintf(&name, "%s-%s", pname, target->t_name); free(pname); if (ret <= 0) log_err(1, "asprintf"); if (port_find(conf, name) != NULL) { log_warnx("duplicate port \"%s\"", name); free(name); return (NULL); } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); port->p_conf = conf; port->p_name = name; port->p_ioctl_port = 1; port->p_ioctl_pp = pp; port->p_ioctl_vp = vp; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; return (port); } struct port * port_new_pp(struct conf *conf, struct target *target, struct pport *pp) { struct port *port; char *name; int ret; ret = asprintf(&name, "%s-%s", pp->pp_name, target->t_name); if (ret <= 0) log_err(1, "asprintf"); if (port_find(conf, name) != NULL) { log_warnx("duplicate port \"%s\"", name); free(name); return (NULL); } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); port->p_conf = conf; port->p_name = name; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; TAILQ_INSERT_TAIL(&pp->pp_ports, port, p_pps); port->p_pport = pp; return (port); } struct port * port_find(const struct conf *conf, const char *name) { struct port *port; TAILQ_FOREACH(port, &conf->conf_ports, p_next) { if (strcasecmp(port->p_name, name) == 0) return (port); } return (NULL); } struct port * port_find_in_pg(const struct portal_group *pg, const char *target) { struct port *port; TAILQ_FOREACH(port, &pg->pg_ports, p_pgs) { if (strcasecmp(port->p_target->t_name, target) == 0) return (port); } return (NULL); } void port_delete(struct port *port) { if (port->p_portal_group) TAILQ_REMOVE(&port->p_portal_group->pg_ports, port, p_pgs); if (port->p_pport) TAILQ_REMOVE(&port->p_pport->pp_ports, port, p_pps); if (port->p_target) TAILQ_REMOVE(&port->p_target->t_ports, port, p_ts); TAILQ_REMOVE(&port->p_conf->conf_ports, port, p_next); free(port->p_name); free(port); } int port_is_dummy(struct port *port) { if (port->p_portal_group) { if (port->p_portal_group->pg_foreign) return (1); if (TAILQ_EMPTY(&port->p_portal_group->pg_portals)) return (1); } return (0); } struct target * target_new(struct conf *conf, const char *name) { struct target *targ; int i, len; targ = target_find(conf, name); if (targ != NULL) { log_warnx("duplicated target \"%s\"", name); return (NULL); } if (valid_iscsi_name(name) == false) { log_warnx("target name \"%s\" is invalid", name); return (NULL); } targ = calloc(1, sizeof(*targ)); if (targ == NULL) log_err(1, "calloc"); targ->t_name = checked_strdup(name); /* * RFC 3722 requires us to normalize the name to lowercase. */ len = strlen(name); for (i = 0; i < len; i++) targ->t_name[i] = tolower(targ->t_name[i]); targ->t_conf = conf; TAILQ_INIT(&targ->t_ports); TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); return (targ); } void target_delete(struct target *targ) { struct port *port, *tport; TAILQ_FOREACH_SAFE(port, &targ->t_ports, p_ts, tport) port_delete(port); TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); free(targ->t_name); free(targ->t_redirection); free(targ); } struct target * target_find(struct conf *conf, const char *name) { struct target *targ; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (strcasecmp(targ->t_name, name) == 0) return (targ); } 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 conf *conf, const char *name) { struct lun *lun; lun = lun_find(conf, name); if (lun != NULL) { log_warnx("duplicated lun \"%s\"", name); return (NULL); } lun = calloc(1, sizeof(*lun)); if (lun == NULL) log_err(1, "calloc"); lun->l_conf = conf; lun->l_name = checked_strdup(name); TAILQ_INIT(&lun->l_options); TAILQ_INSERT_TAIL(&conf->conf_luns, lun, l_next); lun->l_ctl_lun = -1; return (lun); } void lun_delete(struct lun *lun) { struct target *targ; struct option *o, *tmp; int i; TAILQ_FOREACH(targ, &lun->l_conf->conf_targets, t_next) { for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] == lun) targ->t_luns[i] = NULL; } } TAILQ_REMOVE(&lun->l_conf->conf_luns, lun, l_next); TAILQ_FOREACH_SAFE(o, &lun->l_options, o_next, tmp) option_delete(&lun->l_options, o); free(lun->l_name); free(lun->l_backend); free(lun->l_device_id); free(lun->l_path); free(lun->l_scsiname); free(lun->l_serial); free(lun); } struct lun * lun_find(const struct conf *conf, const char *name) { struct lun *lun; TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { if (strcmp(lun->l_name, name) == 0) return (lun); } return (NULL); } void lun_set_backend(struct lun *lun, const char *value) { free(lun->l_backend); lun->l_backend = checked_strdup(value); } void lun_set_blocksize(struct lun *lun, size_t value) { lun->l_blocksize = value; } void lun_set_device_type(struct lun *lun, uint8_t value) { lun->l_device_type = value; } void lun_set_device_id(struct lun *lun, const char *value) { free(lun->l_device_id); lun->l_device_id = checked_strdup(value); } void lun_set_path(struct lun *lun, const char *value) { free(lun->l_path); lun->l_path = checked_strdup(value); } void lun_set_scsiname(struct lun *lun, const char *value) { free(lun->l_scsiname); lun->l_scsiname = checked_strdup(value); } void lun_set_serial(struct lun *lun, const char *value) { free(lun->l_serial); lun->l_serial = checked_strdup(value); } void lun_set_size(struct lun *lun, size_t value) { lun->l_size = value; } void lun_set_ctl_lun(struct lun *lun, uint32_t value) { lun->l_ctl_lun = value; } struct option * option_new(struct options *options, const char *name, const char *value) { struct option *o; o = option_find(options, name); if (o != NULL) { log_warnx("duplicated option \"%s\"", name); return (NULL); } o = calloc(1, sizeof(*o)); if (o == NULL) log_err(1, "calloc"); o->o_name = checked_strdup(name); o->o_value = checked_strdup(value); TAILQ_INSERT_TAIL(options, o, o_next); return (o); } void option_delete(struct options *options, struct option *o) { TAILQ_REMOVE(options, o, o_next); free(o->o_name); free(o->o_value); free(o); } struct option * option_find(const struct options *options, const char *name) { struct option *o; TAILQ_FOREACH(o, options, o_next) { if (strcmp(o->o_name, name) == 0) return (o); } return (NULL); } void option_set(struct option *o, const char *value) { free(o->o_value); o->o_value = checked_strdup(value); } static struct connection * connection_new(struct portal *portal, int fd, const char *host, const struct sockaddr *client_sa) { struct connection *conn; conn = calloc(1, sizeof(*conn)); if (conn == NULL) log_err(1, "calloc"); conn->conn_portal = portal; conn->conn_socket = fd; conn->conn_initiator_addr = checked_strdup(host); memcpy(&conn->conn_initiator_sa, client_sa, client_sa->sa_len); /* * Default values, from RFC 3720, section 12. */ conn->conn_max_recv_data_segment_length = 8192; conn->conn_max_send_data_segment_length = 8192; conn->conn_max_burst_length = 262144; conn->conn_first_burst_length = 65536; conn->conn_immediate_data = true; return (conn); } #if 0 static void conf_print(struct conf *conf) { struct auth_group *ag; struct auth *auth; struct auth_name *auth_name; struct auth_portal *auth_portal; struct portal_group *pg; struct portal *portal; struct target *targ; struct lun *lun; struct option *o; TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { fprintf(stderr, "auth-group %s {\n", ag->ag_name); TAILQ_FOREACH(auth, &ag->ag_auths, a_next) fprintf(stderr, "\t chap-mutual %s %s %s %s\n", auth->a_user, auth->a_secret, auth->a_mutual_user, auth->a_mutual_secret); TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) fprintf(stderr, "\t initiator-name %s\n", auth_name->an_initator_name); TAILQ_FOREACH(auth_portal, &ag->ag_portals, ap_next) fprintf(stderr, "\t initiator-portal %s\n", auth_portal->ap_initator_portal); fprintf(stderr, "}\n"); } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { fprintf(stderr, "portal-group %s {\n", pg->pg_name); TAILQ_FOREACH(portal, &pg->pg_portals, p_next) fprintf(stderr, "\t listen %s\n", portal->p_listen); fprintf(stderr, "}\n"); } TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { fprintf(stderr, "\tlun %s {\n", lun->l_name); fprintf(stderr, "\t\tpath %s\n", lun->l_path); TAILQ_FOREACH(o, &lun->l_options, o_next) fprintf(stderr, "\t\toption %s %s\n", o->o_name, o->o_value); fprintf(stderr, "\t}\n"); } TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { fprintf(stderr, "target %s {\n", targ->t_name); if (targ->t_alias != NULL) fprintf(stderr, "\t alias %s\n", targ->t_alias); fprintf(stderr, "}\n"); } } #endif static int conf_verify_lun(struct lun *lun) { const struct lun *lun2; if (lun->l_backend == NULL) lun_set_backend(lun, "block"); if (strcmp(lun->l_backend, "block") == 0) { if (lun->l_path == NULL) { log_warnx("missing path for lun \"%s\"", lun->l_name); return (1); } } else if (strcmp(lun->l_backend, "ramdisk") == 0) { if (lun->l_size == 0) { log_warnx("missing size for ramdisk-backed lun \"%s\"", lun->l_name); return (1); } if (lun->l_path != NULL) { log_warnx("path must not be specified " "for ramdisk-backed lun \"%s\"", lun->l_name); return (1); } } if (lun->l_blocksize == 0) { if (lun->l_device_type == 5) lun_set_blocksize(lun, DEFAULT_CD_BLOCKSIZE); else lun_set_blocksize(lun, DEFAULT_BLOCKSIZE); } else if (lun->l_blocksize < 0) { log_warnx("invalid blocksize for lun \"%s\"; " "must be larger than 0", lun->l_name); return (1); } if (lun->l_size != 0 && lun->l_size % lun->l_blocksize != 0) { log_warnx("invalid size for lun \"%s\"; " "must be multiple of blocksize", lun->l_name); return (1); } TAILQ_FOREACH(lun2, &lun->l_conf->conf_luns, l_next) { if (lun == lun2) continue; if (lun->l_path != NULL && lun2->l_path != NULL && strcmp(lun->l_path, lun2->l_path) == 0) { log_debugx("WARNING: path \"%s\" duplicated " "between lun \"%s\", and " "lun \"%s\"", lun->l_path, lun->l_name, lun2->l_name); } } return (0); } int conf_verify(struct conf *conf) { struct auth_group *ag; struct portal_group *pg; struct port *port; struct target *targ; struct lun *lun; bool found; int error, i; if (conf->conf_pidfile_path == NULL) conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE); TAILQ_FOREACH(lun, &conf->conf_luns, l_next) { error = conf_verify_lun(lun); if (error != 0) return (error); } TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_auth_group == NULL) { targ->t_auth_group = auth_group_find(conf, "default"); assert(targ->t_auth_group != NULL); } if (TAILQ_EMPTY(&targ->t_ports)) { pg = portal_group_find(conf, "default"); assert(pg != NULL); port_new(conf, targ, pg); } found = false; for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] != NULL) found = true; } 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); if (pg->pg_discovery_auth_group == NULL) { pg->pg_discovery_auth_group = auth_group_find(conf, "default"); assert(pg->pg_discovery_auth_group != NULL); } if (pg->pg_discovery_filter == PG_FILTER_UNKNOWN) pg->pg_discovery_filter = PG_FILTER_NONE; if (pg->pg_redirection != NULL) { if (!TAILQ_EMPTY(&pg->pg_ports)) { log_debugx("portal-group \"%s\" assigned " "to target, but configured " "for redirection", pg->pg_name); } pg->pg_unassigned = false; } else if (!TAILQ_EMPTY(&pg->pg_ports)) { 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; } } TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { if (ag->ag_name == NULL) assert(ag->ag_target != NULL); else assert(ag->ag_target == NULL); found = false; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_auth_group == ag) { found = true; break; } } TAILQ_FOREACH(port, &conf->conf_ports, p_next) { if (port->p_auth_group == ag) { found = true; break; } } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { if (pg->pg_discovery_auth_group == ag) { found = true; break; } } if (!found && ag->ag_name != NULL && strcmp(ag->ag_name, "default") != 0 && strcmp(ag->ag_name, "no-authentication") != 0 && strcmp(ag->ag_name, "no-access") != 0) { log_warnx("auth-group \"%s\" not assigned " "to any target", ag->ag_name); } } return (0); } static int conf_apply(struct conf *oldconf, struct conf *newconf) { struct lun *oldlun, *newlun, *tmplun; struct portal_group *oldpg, *newpg; struct portal *oldp, *newp; struct port *oldport, *newport, *tmpport; struct isns *oldns, *newns; pid_t otherpid; int changed, cumulated_error = 0, error, sockbuf; int one = 1; if (oldconf->conf_debug != newconf->conf_debug) { log_debugx("changing debug level to %d", newconf->conf_debug); log_init(newconf->conf_debug); } if (oldconf->conf_pidfh != NULL) { assert(oldconf->conf_pidfile_path != NULL); if (newconf->conf_pidfile_path != NULL && strcmp(oldconf->conf_pidfile_path, newconf->conf_pidfile_path) == 0) { newconf->conf_pidfh = oldconf->conf_pidfh; oldconf->conf_pidfh = NULL; } else { log_debugx("removing pidfile %s", oldconf->conf_pidfile_path); pidfile_remove(oldconf->conf_pidfh); oldconf->conf_pidfh = NULL; } } if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) { log_debugx("opening pidfile %s", newconf->conf_pidfile_path); newconf->conf_pidfh = pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid); if (newconf->conf_pidfh == NULL) { if (errno == EEXIST) log_errx(1, "daemon already running, pid: %jd.", (intmax_t)otherpid); log_err(1, "cannot open or create pidfile \"%s\"", newconf->conf_pidfile_path); } } /* * Go through the new portal groups, assigning tags or preserving old. */ TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { if (newpg->pg_tag != 0) continue; oldpg = portal_group_find(oldconf, newpg->pg_name); if (oldpg != NULL) newpg->pg_tag = oldpg->pg_tag; else newpg->pg_tag = ++last_portal_group_tag; } /* Deregister on removed iSNS servers. */ TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) { TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) { if (strcmp(oldns->i_addr, newns->i_addr) == 0) break; } if (newns == NULL) isns_deregister(oldns); } /* * XXX: If target or lun removal fails, we should somehow "move" * the old lun or target into newconf, so that subsequent * conf_apply() would try to remove them again. That would * be somewhat hairy, though, and lun deletion failures don't * really happen, so leave it as it is for now. */ /* * First, remove any ports present in the old configuration * and missing in the new one. */ TAILQ_FOREACH_SAFE(oldport, &oldconf->conf_ports, p_next, tmpport) { if (port_is_dummy(oldport)) continue; newport = port_find(newconf, oldport->p_name); if (newport != NULL && !port_is_dummy(newport)) continue; log_debugx("removing port \"%s\"", oldport->p_name); error = kernel_port_remove(oldport); if (error != 0) { log_warnx("failed to remove port %s", oldport->p_name); /* * XXX: Uncomment after fixing the root cause. * * cumulated_error++; */ } } /* * Second, remove any LUNs present in the old configuration * and missing in the new one. */ TAILQ_FOREACH_SAFE(oldlun, &oldconf->conf_luns, l_next, tmplun) { newlun = lun_find(newconf, oldlun->l_name); if (newlun == NULL) { log_debugx("lun \"%s\", CTL lun %d " "not found in new configuration; " "removing", oldlun->l_name, oldlun->l_ctl_lun); error = kernel_lun_remove(oldlun); if (error != 0) { log_warnx("failed to remove lun \"%s\", " "CTL lun %d", oldlun->l_name, oldlun->l_ctl_lun); cumulated_error++; } continue; } /* * Also remove the LUNs changed by more than size. */ changed = 0; assert(oldlun->l_backend != NULL); assert(newlun->l_backend != NULL); if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) { log_debugx("backend for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (oldlun->l_blocksize != newlun->l_blocksize) { log_debugx("blocksize for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_device_id != NULL && (oldlun->l_device_id == NULL || strcmp(oldlun->l_device_id, newlun->l_device_id) != 0)) { log_debugx("device-id for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_path != NULL && (oldlun->l_path == NULL || strcmp(oldlun->l_path, newlun->l_path) != 0)) { log_debugx("path for lun \"%s\", " "CTL lun %d, changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (newlun->l_serial != NULL && (oldlun->l_serial == NULL || strcmp(oldlun->l_serial, newlun->l_serial) != 0)) { log_debugx("serial for lun \"%s\", " "CTL lun %d changed; removing", oldlun->l_name, oldlun->l_ctl_lun); changed = 1; } if (changed) { error = kernel_lun_remove(oldlun); if (error != 0) { log_warnx("failed to remove lun \"%s\", " "CTL lun %d", oldlun->l_name, oldlun->l_ctl_lun); cumulated_error++; } lun_delete(oldlun); continue; } lun_set_ctl_lun(newlun, oldlun->l_ctl_lun); } TAILQ_FOREACH_SAFE(newlun, &newconf->conf_luns, l_next, tmplun) { oldlun = lun_find(oldconf, newlun->l_name); if (oldlun != NULL) { log_debugx("modifying lun \"%s\", CTL lun %d", newlun->l_name, newlun->l_ctl_lun); error = kernel_lun_modify(newlun); if (error != 0) { log_warnx("failed to " "modify lun \"%s\", CTL lun %d", newlun->l_name, newlun->l_ctl_lun); cumulated_error++; } continue; } log_debugx("adding lun \"%s\"", newlun->l_name); error = kernel_lun_add(newlun); if (error != 0) { log_warnx("failed to add lun \"%s\"", newlun->l_name); lun_delete(newlun); cumulated_error++; } } /* * Now add new ports or modify existing ones. */ TAILQ_FOREACH(newport, &newconf->conf_ports, p_next) { if (port_is_dummy(newport)) continue; oldport = port_find(oldconf, newport->p_name); if (oldport == NULL || port_is_dummy(oldport)) { log_debugx("adding port \"%s\"", newport->p_name); error = kernel_port_add(newport); } else { log_debugx("updating port \"%s\"", newport->p_name); newport->p_ctl_port = oldport->p_ctl_port; error = kernel_port_update(newport, oldport); } if (error != 0) { log_warnx("failed to %s port %s", (oldport == NULL) ? "add" : "update", newport->p_name); /* * XXX: Uncomment after fixing the root cause. * * cumulated_error++; */ } } /* * Go through the new portals, opening the sockets as necessary. */ TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { if (newpg->pg_foreign) continue; if (newpg->pg_unassigned) { log_debugx("not listening on portal-group \"%s\", " "not assigned to any target", newpg->pg_name); continue; } TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) { /* * Try to find already open portal and reuse * the listening socket. We don't care about * what portal or portal group that was, what * matters is the listening address. */ TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { if (strcmp(newp->p_listen, oldp->p_listen) == 0 && oldp->p_socket > 0) { newp->p_socket = oldp->p_socket; oldp->p_socket = 0; break; } } } if (newp->p_socket > 0) { /* * We're done with this portal. */ continue; } #ifdef ICL_KERNEL_PROXY if (proxy_mode) { newpg->pg_conf->conf_portal_id++; newp->p_id = newpg->pg_conf->conf_portal_id; log_debugx("listening on %s, portal-group " "\"%s\", portal id %d, using ICL proxy", newp->p_listen, newpg->pg_name, newp->p_id); kernel_listen(newp->p_ai, newp->p_iser, newp->p_id); continue; } #endif assert(proxy_mode == false); assert(newp->p_iser == false); log_debugx("listening on %s, portal-group \"%s\"", newp->p_listen, newpg->pg_name); newp->p_socket = socket(newp->p_ai->ai_family, newp->p_ai->ai_socktype, newp->p_ai->ai_protocol); if (newp->p_socket < 0) { log_warn("socket(2) failed for %s", newp->p_listen); cumulated_error++; continue; } sockbuf = SOCKBUF_SIZE; if (setsockopt(newp->p_socket, SOL_SOCKET, SO_RCVBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_RCVBUF) failed " "for %s", newp->p_listen); sockbuf = SOCKBUF_SIZE; if (setsockopt(newp->p_socket, SOL_SOCKET, SO_SNDBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_SNDBUF) failed " "for %s", newp->p_listen); error = setsockopt(newp->p_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); if (error != 0) { log_warn("setsockopt(SO_REUSEADDR) failed " "for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; + } + if (newpg->pg_dscp != -1) { + struct sockaddr sa; + int len = sizeof(sa); + getsockname(newp->p_socket, &sa, &len); + /* + * Only allow the 6-bit DSCP + * field to be modified + */ + int tos = newpg->pg_dscp << 2; + if (sa.sa_family == AF_INET) { + if (setsockopt(newp->p_socket, + IPPROTO_IP, IP_TOS, + &tos, sizeof(tos)) == -1) + log_warn("setsockopt(IP_TOS) " + "failed for %s", + newp->p_listen); + } else + if (sa.sa_family == AF_INET6) { + if (setsockopt(newp->p_socket, + IPPROTO_IPV6, IPV6_TCLASS, + &tos, sizeof(tos)) == -1) + log_warn("setsockopt(IPV6_TCLASS) " + "failed for %s", + newp->p_listen); + } } error = bind(newp->p_socket, newp->p_ai->ai_addr, newp->p_ai->ai_addrlen); if (error != 0) { log_warn("bind(2) failed for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; } error = listen(newp->p_socket, -1); if (error != 0) { log_warn("listen(2) failed for %s", newp->p_listen); close(newp->p_socket); newp->p_socket = 0; cumulated_error++; continue; } } } /* * Go through the no longer used sockets, closing them. */ TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { if (oldp->p_socket <= 0) continue; log_debugx("closing socket for %s, portal-group \"%s\"", oldp->p_listen, oldpg->pg_name); close(oldp->p_socket); oldp->p_socket = 0; } } /* (Re-)Register on remaining/new iSNS servers. */ TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) { TAILQ_FOREACH(oldns, &oldconf->conf_isns, i_next) { if (strcmp(oldns->i_addr, newns->i_addr) == 0) break; } isns_register(newns, oldns); } /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) set_timeout((newconf->conf_isns_period + 2) / 3, false); return (cumulated_error); } bool timed_out(void) { return (sigalrm_received); } static void sigalrm_handler_fatal(int dummy __unused) { /* * It would be easiest to just log an error and exit. We can't * do this, though, because log_errx() is not signal safe, since * it calls syslog(3). Instead, set a flag checked by pdu_send() * and pdu_receive(), to call log_errx() there. Should they fail * to notice, we'll exit here one second later. */ if (sigalrm_received) { /* * Oh well. Just give up and quit. */ _exit(2); } sigalrm_received = true; } static void sigalrm_handler(int dummy __unused) { sigalrm_received = true; } void set_timeout(int timeout, int fatal) { struct sigaction sa; struct itimerval itv; int error; if (timeout <= 0) { log_debugx("session timeout disabled"); bzero(&itv, sizeof(itv)); error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); sigalrm_received = false; return; } sigalrm_received = false; bzero(&sa, sizeof(sa)); if (fatal) sa.sa_handler = sigalrm_handler_fatal; else sa.sa_handler = sigalrm_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGALRM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); /* * First SIGALRM will arive after conf_timeout seconds. * If we do nothing, another one will arrive a second later. */ log_debugx("setting session timeout to %d seconds", timeout); bzero(&itv, sizeof(itv)); itv.it_interval.tv_sec = 1; itv.it_value.tv_sec = timeout; error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); } static int wait_for_children(bool block) { pid_t pid; int status; int num = 0; for (;;) { /* * If "block" is true, wait for at least one process. */ if (block && num == 0) pid = wait4(-1, &status, 0, NULL); else pid = wait4(-1, &status, WNOHANG, NULL); if (pid <= 0) break; if (WIFSIGNALED(status)) { log_warnx("child process %d terminated with signal %d", pid, WTERMSIG(status)); } else if (WEXITSTATUS(status) != 0) { log_warnx("child process %d terminated with exit status %d", pid, WEXITSTATUS(status)); } else { log_debugx("child process %d terminated gracefully", pid); } num++; } return (num); } static void handle_connection(struct portal *portal, int fd, const struct sockaddr *client_sa, bool dont_fork) { struct connection *conn; int error; pid_t pid; char host[NI_MAXHOST + 1]; struct conf *conf; conf = portal->p_portal_group->pg_conf; if (dont_fork) { log_debugx("incoming connection; not forking due to -d flag"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", conf->conf_maxproc); nchildren -= wait_for_children(true); assert(nchildren >= 0); } log_debugx("incoming connection; forking child process #%d", nchildren); nchildren++; pid = fork(); if (pid < 0) log_err(1, "fork"); if (pid > 0) { close(fd); return; } } pidfile_close(conf->conf_pidfh); error = getnameinfo(client_sa, client_sa->sa_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); if (error != 0) log_errx(1, "getnameinfo: %s", gai_strerror(error)); log_debugx("accepted connection from %s; portal group \"%s\"", host, portal->p_portal_group->pg_name); log_set_peer_addr(host); setproctitle("%s", host); conn = connection_new(portal, fd, host, client_sa); set_timeout(conf->conf_timeout, true); kernel_capsicate(); login(conn); if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { kernel_handoff(conn); log_debugx("connection handed off to the kernel"); } else { assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); discovery(conn); } log_debugx("nothing more to do; exiting"); exit(0); } static int fd_add(int fd, fd_set *fdset, int nfds) { /* * Skip sockets which we failed to bind. */ if (fd <= 0) return (nfds); FD_SET(fd, fdset); if (fd > nfds) nfds = fd; return (nfds); } static void main_loop(struct conf *conf, bool dont_fork) { struct portal_group *pg; struct portal *portal; struct sockaddr_storage client_sa; socklen_t client_salen; #ifdef ICL_KERNEL_PROXY int connection_id; int portal_id; #endif fd_set fdset; int error, nfds, client_fd; pidfile_write(conf->conf_pidfh); for (;;) { if (sighup_received || sigterm_received || timed_out()) return; #ifdef ICL_KERNEL_PROXY if (proxy_mode) { client_salen = sizeof(client_sa); kernel_accept(&connection_id, &portal_id, (struct sockaddr *)&client_sa, &client_salen); assert(client_salen >= client_sa.ss_len); log_debugx("incoming connection, id %d, portal id %d", connection_id, portal_id); TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { if (portal->p_id == portal_id) { goto found; } } } log_errx(1, "kernel returned invalid portal_id %d", portal_id); found: handle_connection(portal, connection_id, (struct sockaddr *)&client_sa, dont_fork); } else { #endif assert(proxy_mode == false); FD_ZERO(&fdset); nfds = 0; TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) nfds = fd_add(portal->p_socket, &fdset, nfds); } error = select(nfds + 1, &fdset, NULL, NULL, NULL); if (error <= 0) { if (errno == EINTR) return; log_err(1, "select"); } TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { if (!FD_ISSET(portal->p_socket, &fdset)) continue; client_salen = sizeof(client_sa); client_fd = accept(portal->p_socket, (struct sockaddr *)&client_sa, &client_salen); if (client_fd < 0) { if (errno == ECONNABORTED) continue; log_err(1, "accept"); } assert(client_salen >= client_sa.ss_len); handle_connection(portal, client_fd, (struct sockaddr *)&client_sa, dont_fork); break; } } #ifdef ICL_KERNEL_PROXY } #endif } } static void sighup_handler(int dummy __unused) { sighup_received = true; } static void sigterm_handler(int dummy __unused) { sigterm_received = true; } static void sigchld_handler(int dummy __unused) { /* * The only purpose of this handler is to make SIGCHLD * interrupt the ISCSIDWAIT ioctl(2), so we can call * wait_for_children(). */ } static void register_signals(void) { struct sigaction sa; int error; bzero(&sa, sizeof(sa)); sa.sa_handler = sighup_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGHUP, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigterm_handler; error = sigaction(SIGTERM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigterm_handler; error = sigaction(SIGINT, &sa, NULL); if (error != 0) log_err(1, "sigaction"); sa.sa_handler = sigchld_handler; error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } 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? */ } static struct conf * conf_new_from_file(const char *path, struct conf *oldconf, bool ucl) { struct conf *conf; struct auth_group *ag; struct portal_group *pg; struct pport *pp; int error; log_debugx("obtaining configuration from %s", path); conf = conf_new(); TAILQ_FOREACH(pp, &oldconf->conf_pports, pp_next) pport_copy(pp, conf); 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); if (ucl) error = uclparse_conf(conf, path); else error = parse_conf(conf, path); if (error != 0) { conf_delete(conf); return (NULL); } check_perms(path); 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); } int main(int argc, char **argv) { struct conf *oldconf, *newconf, *tmpconf; struct isns *newns; const char *config_path = DEFAULT_CONFIG_PATH; int debug = 0, ch, error; bool dont_daemonize = false; bool use_ucl = false; while ((ch = getopt(argc, argv, "duf:R")) != -1) { switch (ch) { case 'd': dont_daemonize = true; debug++; break; case 'u': use_ucl = true; break; case 'f': config_path = optarg; break; case 'R': #ifndef ICL_KERNEL_PROXY log_errx(1, "ctld(8) compiled without ICL_KERNEL_PROXY " "does not support iSER protocol"); #endif proxy_mode = true; break; case '?': default: usage(); } } argc -= optind; if (argc != 0) usage(); log_init(debug); kernel_init(); oldconf = conf_new_from_kernel(); newconf = conf_new_from_file(config_path, oldconf, use_ucl); if (newconf == NULL) log_errx(1, "configuration error; exiting"); if (debug > 0) { oldconf->conf_debug = debug; newconf->conf_debug = debug; } error = conf_apply(oldconf, newconf); if (error != 0) log_errx(1, "failed to apply configuration; exiting"); conf_delete(oldconf); oldconf = NULL; register_signals(); if (dont_daemonize == false) { log_debugx("daemonizing"); if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(newconf->conf_pidfh); exit(1); } } /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) set_timeout((newconf->conf_isns_period + 2) / 3, false); for (;;) { main_loop(newconf, dont_daemonize); if (sighup_received) { sighup_received = false; log_debugx("received SIGHUP, reloading configuration"); tmpconf = conf_new_from_file(config_path, newconf, use_ucl); if (tmpconf == NULL) { log_warnx("configuration error, " "continuing with old configuration"); } else { if (debug > 0) tmpconf->conf_debug = debug; oldconf = newconf; newconf = tmpconf; error = conf_apply(oldconf, newconf); if (error != 0) log_warnx("failed to reload " "configuration"); conf_delete(oldconf); oldconf = NULL; } } else if (sigterm_received) { log_debugx("exiting on signal; " "reloading empty configuration"); log_debugx("removing CTL iSCSI ports " "and terminating all connections"); oldconf = newconf; newconf = conf_new(); if (debug > 0) newconf->conf_debug = debug; error = conf_apply(oldconf, newconf); if (error != 0) log_warnx("failed to apply configuration"); conf_delete(oldconf); oldconf = NULL; log_warnx("exiting on signal"); exit(0); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); if (timed_out()) { set_timeout(0, false); TAILQ_FOREACH(newns, &newconf->conf_isns, i_next) isns_check(newns); /* Schedule iSNS update */ if (!TAILQ_EMPTY(&newconf->conf_isns)) { set_timeout((newconf->conf_isns_period + 2) / 3, false); } } } } /* NOTREACHED */ } Index: stable/12/usr.sbin/ctld/ctld.h =================================================================== --- stable/12/usr.sbin/ctld/ctld.h (revision 366626) +++ stable/12/usr.sbin/ctld/ctld.h (revision 366627) @@ -1,471 +1,472 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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$ */ #ifndef CTLD_H #define CTLD_H #include #ifdef ICL_KERNEL_PROXY #include #endif #include #include #include #define DEFAULT_CONFIG_PATH "/etc/ctl.conf" #define DEFAULT_PIDFILE "/var/run/ctld.pid" #define DEFAULT_BLOCKSIZE 512 #define DEFAULT_CD_BLOCKSIZE 2048 #define MAX_LUNS 1024 #define MAX_NAME_LEN 223 #define MAX_DATA_SEGMENT_LENGTH (128 * 1024) #define SOCKBUF_SIZE 1048576 struct auth { TAILQ_ENTRY(auth) a_next; struct auth_group *a_auth_group; char *a_user; char *a_secret; char *a_mutual_user; char *a_mutual_secret; }; struct auth_name { TAILQ_ENTRY(auth_name) an_next; struct auth_group *an_auth_group; char *an_initator_name; }; struct auth_portal { TAILQ_ENTRY(auth_portal) ap_next; struct auth_group *ap_auth_group; char *ap_initator_portal; struct sockaddr_storage ap_sa; int ap_mask; }; #define AG_TYPE_UNKNOWN 0 #define AG_TYPE_DENY 1 #define AG_TYPE_NO_AUTHENTICATION 2 #define AG_TYPE_CHAP 3 #define AG_TYPE_CHAP_MUTUAL 4 struct auth_group { TAILQ_ENTRY(auth_group) ag_next; struct conf *ag_conf; char *ag_name; struct target *ag_target; int ag_type; TAILQ_HEAD(, auth) ag_auths; TAILQ_HEAD(, auth_name) ag_names; TAILQ_HEAD(, auth_portal) ag_portals; }; struct portal { TAILQ_ENTRY(portal) p_next; struct portal_group *p_portal_group; bool p_iser; char *p_listen; struct addrinfo *p_ai; #ifdef ICL_KERNEL_PROXY int p_id; #endif TAILQ_HEAD(, target) p_targets; int p_socket; }; TAILQ_HEAD(options, option); #define PG_FILTER_UNKNOWN 0 #define PG_FILTER_NONE 1 #define PG_FILTER_PORTAL 2 #define PG_FILTER_PORTAL_NAME 3 #define PG_FILTER_PORTAL_NAME_AUTH 4 struct portal_group { TAILQ_ENTRY(portal_group) pg_next; struct conf *pg_conf; struct options pg_options; char *pg_name; struct auth_group *pg_discovery_auth_group; int pg_discovery_filter; int pg_foreign; bool pg_unassigned; TAILQ_HEAD(, portal) pg_portals; TAILQ_HEAD(, port) pg_ports; char *pg_offload; char *pg_redirection; + int pg_dscp; uint16_t pg_tag; }; struct pport { TAILQ_ENTRY(pport) pp_next; TAILQ_HEAD(, port) pp_ports; struct conf *pp_conf; char *pp_name; uint32_t pp_ctl_port; }; struct port { TAILQ_ENTRY(port) p_next; TAILQ_ENTRY(port) p_pgs; TAILQ_ENTRY(port) p_pps; TAILQ_ENTRY(port) p_ts; struct conf *p_conf; char *p_name; struct auth_group *p_auth_group; struct portal_group *p_portal_group; struct pport *p_pport; struct target *p_target; int p_ioctl_port; int p_ioctl_pp; int p_ioctl_vp; uint32_t p_ctl_port; }; struct option { TAILQ_ENTRY(option) o_next; char *o_name; char *o_value; }; struct lun { TAILQ_ENTRY(lun) l_next; struct conf *l_conf; struct options l_options; char *l_name; char *l_backend; uint8_t l_device_type; int l_blocksize; char *l_device_id; char *l_path; char *l_scsiname; char *l_serial; int64_t l_size; int l_ctl_lun; }; struct target { TAILQ_ENTRY(target) t_next; struct conf *t_conf; struct lun *t_luns[MAX_LUNS]; struct auth_group *t_auth_group; TAILQ_HEAD(, port) t_ports; char *t_name; char *t_alias; char *t_redirection; }; struct isns { TAILQ_ENTRY(isns) i_next; struct conf *i_conf; char *i_addr; struct addrinfo *i_ai; }; struct conf { char *conf_pidfile_path; TAILQ_HEAD(, lun) conf_luns; TAILQ_HEAD(, target) conf_targets; TAILQ_HEAD(, auth_group) conf_auth_groups; TAILQ_HEAD(, port) conf_ports; TAILQ_HEAD(, portal_group) conf_portal_groups; TAILQ_HEAD(, pport) conf_pports; TAILQ_HEAD(, isns) conf_isns; int conf_isns_period; int conf_isns_timeout; int conf_debug; int conf_timeout; int conf_maxproc; #ifdef ICL_KERNEL_PROXY int conf_portal_id; #endif struct pidfh *conf_pidfh; bool conf_default_pg_defined; bool conf_default_ag_defined; bool conf_kernel_port_on; }; #define CONN_SESSION_TYPE_NONE 0 #define CONN_SESSION_TYPE_DISCOVERY 1 #define CONN_SESSION_TYPE_NORMAL 2 #define CONN_DIGEST_NONE 0 #define CONN_DIGEST_CRC32C 1 struct connection { struct portal *conn_portal; struct port *conn_port; struct target *conn_target; int conn_socket; int conn_session_type; char *conn_initiator_name; char *conn_initiator_addr; char *conn_initiator_alias; uint8_t conn_initiator_isid[6]; struct sockaddr_storage conn_initiator_sa; uint32_t conn_cmdsn; uint32_t conn_statsn; int conn_max_recv_data_segment_limit; int conn_max_send_data_segment_limit; int conn_max_burst_limit; int conn_first_burst_limit; int conn_max_recv_data_segment_length; int conn_max_send_data_segment_length; int conn_max_burst_length; int conn_first_burst_length; int conn_immediate_data; int conn_header_digest; int conn_data_digest; const char *conn_user; struct chap *conn_chap; }; struct pdu { struct connection *pdu_connection; struct iscsi_bhs *pdu_bhs; char *pdu_data; size_t pdu_data_len; }; #define KEYS_MAX 1024 struct keys { char *keys_names[KEYS_MAX]; char *keys_values[KEYS_MAX]; char *keys_data; size_t keys_data_len; }; #define CHAP_CHALLENGE_LEN 1024 #define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */ struct chap { unsigned char chap_id; char chap_challenge[CHAP_CHALLENGE_LEN]; char chap_response[CHAP_DIGEST_LEN]; }; struct rchap { char *rchap_secret; unsigned char rchap_id; void *rchap_challenge; size_t rchap_challenge_len; }; struct chap *chap_new(void); char *chap_get_id(const struct chap *chap); char *chap_get_challenge(const struct chap *chap); int chap_receive(struct chap *chap, const char *response); int chap_authenticate(struct chap *chap, const char *secret); void chap_delete(struct chap *chap); struct rchap *rchap_new(const char *secret); int rchap_receive(struct rchap *rchap, const char *id, const char *challenge); char *rchap_get_response(struct rchap *rchap); void rchap_delete(struct rchap *rchap); int parse_conf(struct conf *conf, const char *path); int uclparse_conf(struct conf *conf, const char *path); struct conf *conf_new(void); struct conf *conf_new_from_kernel(void); void conf_delete(struct conf *conf); int conf_verify(struct conf *conf); struct auth_group *auth_group_new(struct conf *conf, const char *name); void auth_group_delete(struct auth_group *ag); struct auth_group *auth_group_find(const struct conf *conf, const char *name); int auth_group_set_type(struct auth_group *ag, const char *type); const struct auth *auth_new_chap(struct auth_group *ag, const char *user, const char *secret); const struct auth *auth_new_chap_mutual(struct auth_group *ag, const char *user, const char *secret, const char *user2, const char *secret2); const struct auth *auth_find(const struct auth_group *ag, const char *user); const struct auth_name *auth_name_new(struct auth_group *ag, const char *initiator_name); bool auth_name_defined(const struct auth_group *ag); const struct auth_name *auth_name_find(const struct auth_group *ag, const char *initiator_name); int auth_name_check(const struct auth_group *ag, const char *initiator_name); const struct auth_portal *auth_portal_new(struct auth_group *ag, const char *initiator_portal); bool auth_portal_defined(const struct auth_group *ag); const struct auth_portal *auth_portal_find(const struct auth_group *ag, const struct sockaddr_storage *sa); int auth_portal_check(const struct auth_group *ag, const struct sockaddr_storage *sa); struct portal_group *portal_group_new(struct conf *conf, const char *name); void portal_group_delete(struct portal_group *pg); struct portal_group *portal_group_find(const struct conf *conf, const char *name); 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_offload(struct portal_group *pg, const char *offload); 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); void isns_register(struct isns *isns, struct isns *oldisns); void isns_check(struct isns *isns); void isns_deregister(struct isns *isns); struct pport *pport_new(struct conf *conf, const char *name, uint32_t ctl_port); struct pport *pport_find(const struct conf *conf, const char *name); struct pport *pport_copy(struct pport *pport, struct conf *conf); void pport_delete(struct pport *pport); struct port *port_new(struct conf *conf, struct target *target, struct portal_group *pg); struct port *port_new_ioctl(struct conf *conf, struct target *target, int pp, int vp); struct port *port_new_pp(struct conf *conf, struct target *target, struct pport *pp); struct port *port_find(const struct conf *conf, const char *name); struct port *port_find_in_pg(const struct portal_group *pg, const char *target); void port_delete(struct port *port); int port_is_dummy(struct port *port); 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 conf *conf, const char *name); void lun_delete(struct lun *lun); struct lun *lun_find(const struct conf *conf, const char *name); void lun_set_backend(struct lun *lun, const char *value); void lun_set_device_type(struct lun *lun, uint8_t value); void lun_set_blocksize(struct lun *lun, size_t value); void lun_set_device_id(struct lun *lun, const char *value); void lun_set_path(struct lun *lun, const char *value); void lun_set_scsiname(struct lun *lun, const char *value); void lun_set_serial(struct lun *lun, const char *value); void lun_set_size(struct lun *lun, size_t value); void lun_set_ctl_lun(struct lun *lun, uint32_t value); struct option *option_new(struct options *os, const char *name, const char *value); void option_delete(struct options *os, struct option *co); struct option *option_find(const struct options *os, const char *name); void option_set(struct option *o, const char *value); void kernel_init(void); int kernel_lun_add(struct lun *lun); int kernel_lun_modify(struct lun *lun); int kernel_lun_remove(struct lun *lun); void kernel_handoff(struct connection *conn); void kernel_limits(const char *offload, int *max_recv_data_segment_length, int *max_send_data_segment_length, int *max_burst_length, int *first_burst_length); int kernel_port_add(struct port *port); int kernel_port_update(struct port *port, struct port *old); int kernel_port_remove(struct port *port); void kernel_capsicate(void); #ifdef ICL_KERNEL_PROXY void kernel_listen(struct addrinfo *ai, bool iser, int portal_id); void kernel_accept(int *connection_id, int *portal_id, struct sockaddr *client_sa, socklen_t *client_salen); void kernel_send(struct pdu *pdu); void kernel_receive(struct pdu *pdu); #endif struct keys *keys_new(void); void keys_delete(struct keys *keys); void keys_load(struct keys *keys, const struct pdu *pdu); void keys_save(struct keys *keys, struct pdu *pdu); const char *keys_find(struct keys *keys, const char *name); void keys_add(struct keys *keys, const char *name, const char *value); void keys_add_int(struct keys *keys, const char *name, int value); struct pdu *pdu_new(struct connection *conn); struct pdu *pdu_new_response(struct pdu *request); void pdu_delete(struct pdu *pdu); void pdu_receive(struct pdu *request); void pdu_send(struct pdu *response); void login(struct connection *conn); void discovery(struct connection *conn); void log_init(int level); void log_set_peer_name(const char *name); void log_set_peer_addr(const char *addr); void log_err(int, const char *, ...) __dead2 __printflike(2, 3); void log_errx(int, const char *, ...) __dead2 __printflike(2, 3); void log_warn(const char *, ...) __printflike(1, 2); void log_warnx(const char *, ...) __printflike(1, 2); void log_debugx(const char *, ...) __printflike(1, 2); char *checked_strdup(const char *); bool valid_iscsi_name(const char *name); void set_timeout(int timeout, int fatal); bool timed_out(void); #endif /* !CTLD_H */ Index: stable/12/usr.sbin/ctld/parse.y =================================================================== --- stable/12/usr.sbin/ctld/parse.y (revision 366626) +++ stable/12/usr.sbin/ctld/parse.y (revision 366627) @@ -1,1094 +1,1144 @@ %{ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 #include #include #include #include #include #include #include "ctld.h" +#include +#include 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 CTL_LUN DEBUG DEVICE_ID DEVICE_TYPE -%token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER FOREIGN +%token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER DSCP FOREIGN %token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT %token LISTEN LISTEN_ISER LUN MAXPROC OFFLOAD OPENING_BRACKET OPTION %token PATH PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR %token TAG TARGET TIMEOUT +%token AF11 AF12 AF13 AF21 AF22 AF23 AF31 AF32 AF33 AF41 AF42 AF43 +%token BE EF CS0 CS1 CS2 CS3 CS4 CS5 CS6 CS7 %union { char *str; } %token 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_foreign | portal_group_listen | portal_group_listen_iser | portal_group_offload | portal_group_option | portal_group_redirect | portal_group_tag + | + portal_group_dscp ; 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_foreign: FOREIGN { portal_group->pg_foreign = 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_offload: OFFLOAD STR { int error; error = portal_group_set_offload(portal_group, $2); free($2); if (error != 0) return (1); } ; portal_group_option: OPTION STR STR { struct option *o; o = option_new(&portal_group->pg_options, $2, $3); free($2); free($3); if (o == NULL) return (1); } ; portal_group_redirect: REDIRECT STR { int error; error = portal_group_set_redirection(portal_group, $2); free($2); if (error != 0) return (1); } ; portal_group_tag: TAG STR { uint64_t tmp; if (expand_number($2, &tmp) != 0) { yyerror("invalid numeric value"); free($2); return (1); } portal_group->pg_tag = tmp; } ; + +portal_group_dscp +: DSCP STR + { + uint64_t tmp; + + if (strcmp($2, "0x") == 0) { + tmp = strtol($2 + 2, NULL, 16); + } else if (expand_number($2, &tmp) != 0) { + yyerror("invalid numeric value"); + free($2); + return(1); + } + if (tmp >= 0x40) { + yyerror("invalid dscp value"); + return(1); + } + + portal_group->pg_dscp = tmp; + } +| DSCP BE { portal_group->pg_dscp = IPTOS_DSCP_CS0 >> 2 ; } +| DSCP EF { portal_group->pg_dscp = IPTOS_DSCP_EF >> 2 ; } +| DSCP CS0 { portal_group->pg_dscp = IPTOS_DSCP_CS0 >> 2 ; } +| DSCP CS1 { portal_group->pg_dscp = IPTOS_DSCP_CS1 >> 2 ; } +| DSCP CS2 { portal_group->pg_dscp = IPTOS_DSCP_CS2 >> 2 ; } +| DSCP CS3 { portal_group->pg_dscp = IPTOS_DSCP_CS3 >> 2 ; } +| DSCP CS4 { portal_group->pg_dscp = IPTOS_DSCP_CS4 >> 2 ; } +| DSCP CS5 { portal_group->pg_dscp = IPTOS_DSCP_CS5 >> 2 ; } +| DSCP CS6 { portal_group->pg_dscp = IPTOS_DSCP_CS6 >> 2 ; } +| DSCP CS7 { portal_group->pg_dscp = IPTOS_DSCP_CS7 >> 2 ; } +| DSCP AF11 { portal_group->pg_dscp = IPTOS_DSCP_AF11 >> 2 ; } +| DSCP AF12 { portal_group->pg_dscp = IPTOS_DSCP_AF12 >> 2 ; } +| DSCP AF13 { portal_group->pg_dscp = IPTOS_DSCP_AF13 >> 2 ; } +| DSCP AF21 { portal_group->pg_dscp = IPTOS_DSCP_AF21 >> 2 ; } +| DSCP AF22 { portal_group->pg_dscp = IPTOS_DSCP_AF22 >> 2 ; } +| DSCP AF23 { portal_group->pg_dscp = IPTOS_DSCP_AF23 >> 2 ; } +| DSCP AF31 { portal_group->pg_dscp = IPTOS_DSCP_AF31 >> 2 ; } +| DSCP AF32 { portal_group->pg_dscp = IPTOS_DSCP_AF32 >> 2 ; } +| DSCP AF33 { portal_group->pg_dscp = IPTOS_DSCP_AF33 >> 2 ; } +| DSCP AF41 { portal_group->pg_dscp = IPTOS_DSCP_AF41 >> 2 ; } +| DSCP AF42 { portal_group->pg_dscp = IPTOS_DSCP_AF42 >> 2 ; } +| DSCP AF43 { portal_group->pg_dscp = IPTOS_DSCP_AF43 >> 2 ; } + ; + 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_portal_group | target_port | 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_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_port: PORT STR { struct pport *pp; struct port *tp; int ret, i_pp, i_vp = 0; ret = sscanf($2, "ioctl/%d/%d", &i_pp, &i_vp); if (ret > 0) { tp = port_new_ioctl(conf, target, i_pp, i_vp); if (tp == NULL) { log_warnx("can't create new ioctl port for " "target \"%s\"", target->t_name); free($2); return (1); } } else { pp = pport_find(conf, $2); if (pp == NULL) { log_warnx("unknown port \"%s\" for target \"%s\"", $2, target->t_name); free($2); return (1); } if (!TAILQ_EMPTY(&pp->pp_ports)) { log_warnx("can't link port \"%s\" to target \"%s\", " "port already linked to some target", $2, target->t_name); free($2); return (1); } tp = port_new_pp(conf, target, pp); if (tp == NULL) { log_warnx("can't link port \"%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; int ret; char *name; if (expand_number($1, &tmp) != 0) { yyerror("invalid numeric value"); free($1); return (1); } if (tmp >= MAX_LUNS) { yyerror("LU number is too big"); free($1); return (1); } ret = asprintf(&name, "%s,lun,%ju", target->t_name, tmp); if (ret <= 0) log_err(1, "asprintf"); 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); if (tmp >= MAX_LUNS) { yyerror("LU number is too big"); free($3); return (1); } 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_device_type | lun_ctl_lun | 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_device_type: DEVICE_TYPE STR { uint64_t tmp; if (strcasecmp($2, "disk") == 0 || strcasecmp($2, "direct") == 0) tmp = 0; else if (strcasecmp($2, "processor") == 0) tmp = 3; else if (strcasecmp($2, "cd") == 0 || strcasecmp($2, "cdrom") == 0 || strcasecmp($2, "dvd") == 0 || strcasecmp($2, "dvdrom") == 0) tmp = 5; else if (expand_number($2, &tmp) != 0 || tmp > 15) { yyerror("invalid numeric value"); free($2); return (1); } lun_set_device_type(lun, tmp); } ; lun_ctl_lun: CTL_LUN STR { uint64_t tmp; if (expand_number($2, &tmp) != 0) { yyerror("invalid numeric value"); free($2); return (1); } if (lun->l_ctl_lun >= 0) { log_warnx("ctl_lun for lun \"%s\" " "specified more than once", lun->l_name); return (1); } lun_set_ctl_lun(lun, tmp); } ; lun_option: OPTION STR STR { struct option *o; o = option_new(&lun->l_options, $2, $3); free($2); free($3); if (o == 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); } int parse_conf(struct conf *newconf, const char *path) { int error; conf = newconf; yyin = fopen(path, "r"); if (yyin == NULL) { log_warn("unable to open configuration file %s", path); return (1); } lineno = 1; yyrestart(yyin); error = yyparse(); auth_group = NULL; portal_group = NULL; target = NULL; lun = NULL; fclose(yyin); return (error); } Index: stable/12/usr.sbin/ctld/token.l =================================================================== --- stable/12/usr.sbin/ctld/token.l (revision 366626) +++ stable/12/usr.sbin/ctld/token.l (revision 366627) @@ -1,99 +1,122 @@ %{ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * 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 #include #include #include "y.tab.h" int lineno; #define YY_DECL int yylex(void) extern int yylex(void); %} %option noyywrap %option noinput %option nounput %% alias { return ALIAS; } auth-group { return AUTH_GROUP; } auth-type { return AUTH_TYPE; } backend { return BACKEND; } blocksize { return BLOCKSIZE; } chap { return CHAP; } chap-mutual { return CHAP_MUTUAL; } ctl-lun { return CTL_LUN; } debug { return DEBUG; } device-id { return DEVICE_ID; } device-type { return DEVICE_TYPE; } discovery-auth-group { return DISCOVERY_AUTH_GROUP; } discovery-filter { return DISCOVERY_FILTER; } +dscp { return DSCP; } foreign { return FOREIGN; } initiator-name { return INITIATOR_NAME; } initiator-portal { return INITIATOR_PORTAL; } listen { return LISTEN; } listen-iser { return LISTEN_ISER; } lun { return LUN; } maxproc { return MAXPROC; } offload { return OFFLOAD; } option { return OPTION; } path { return PATH; } pidfile { return PIDFILE; } isns-server { return ISNS_SERVER; } isns-period { return ISNS_PERIOD; } isns-timeout { return ISNS_TIMEOUT; } port { return PORT; } portal-group { return PORTAL_GROUP; } redirect { return REDIRECT; } serial { return SERIAL; } size { return SIZE; } tag { return TAG; } target { return TARGET; } timeout { return TIMEOUT; } +af11 { return AF11; } +af12 { return AF12; } +af13 { return AF13; } +af21 { return AF21; } +af22 { return AF22; } +af23 { return AF23; } +af31 { return AF31; } +af32 { return AF32; } +af33 { return AF33; } +af41 { return AF41; } +af42 { return AF42; } +af43 { return AF43; } +be { return CS0; } +ef { return EF; } +cs0 { return CS0; } +cs1 { return CS1; } +cs2 { return CS2; } +cs3 { return CS3; } +cs4 { return CS4; } +cs5 { return CS5; } +cs6 { return CS6; } +cs7 { return CS7; } \"[^"]+\" { yylval.str = strndup(yytext + 1, strlen(yytext) - 2); return STR; } [a-zA-Z0-9\.\-@_/\:\[\]]+ { yylval.str = strdup(yytext); return STR; } \{ { return OPENING_BRACKET; } \} { return CLOSING_BRACKET; } #.*$ /* ignore comments */; \r\n { lineno++; } \n { lineno++; } ; { return SEMICOLON; } [ \t]+ /* ignore whitespace */; . { yylval.str = strdup(yytext); return STR; } %% Index: stable/12/usr.sbin/ctld/uclparse.c =================================================================== --- stable/12/usr.sbin/ctld/uclparse.c (revision 366626) +++ stable/12/usr.sbin/ctld/uclparse.c (revision 366627) @@ -1,936 +1,1001 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2015 iXsystems Inc. * All rights reserved. * * This software was developed by Jakub Klama * under sponsorship from iXsystems Inc. * * 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 #include #include #include #include #include #include #include +#include +#include #include "ctld.h" static struct conf *conf = NULL; static int uclparse_toplevel(const ucl_object_t *); static int uclparse_chap(struct auth_group *, const ucl_object_t *); static int uclparse_chap_mutual(struct auth_group *, const ucl_object_t *); static int uclparse_lun(const char *, const ucl_object_t *); static int uclparse_auth_group(const char *, const ucl_object_t *); static int uclparse_portal_group(const char *, const ucl_object_t *); static int uclparse_target(const char *, const ucl_object_t *); static int uclparse_target_portal_group(struct target *, const ucl_object_t *); static int uclparse_target_lun(struct target *, const ucl_object_t *); static int uclparse_chap(struct auth_group *auth_group, const ucl_object_t *obj) { const struct auth *ca; const ucl_object_t *user, *secret; user = ucl_object_find_key(obj, "user"); if (!user || user->type != UCL_STRING) { log_warnx("chap section in auth-group \"%s\" is missing " "\"user\" string key", auth_group->ag_name); return (1); } secret = ucl_object_find_key(obj, "secret"); if (!secret || secret->type != UCL_STRING) { log_warnx("chap section in auth-group \"%s\" is missing " "\"secret\" string key", auth_group->ag_name); } ca = auth_new_chap(auth_group, ucl_object_tostring(user), ucl_object_tostring(secret)); if (ca == NULL) return (1); return (0); } static int uclparse_chap_mutual(struct auth_group *auth_group, const ucl_object_t *obj) { const struct auth *ca; const ucl_object_t *user, *secret, *mutual_user; const ucl_object_t *mutual_secret; user = ucl_object_find_key(obj, "user"); if (!user || user->type != UCL_STRING) { log_warnx("chap-mutual section in auth-group \"%s\" is missing " "\"user\" string key", auth_group->ag_name); return (1); } secret = ucl_object_find_key(obj, "secret"); if (!secret || secret->type != UCL_STRING) { log_warnx("chap-mutual section in auth-group \"%s\" is missing " "\"secret\" string key", auth_group->ag_name); return (1); } mutual_user = ucl_object_find_key(obj, "mutual-user"); if (!user || user->type != UCL_STRING) { log_warnx("chap-mutual section in auth-group \"%s\" is missing " "\"mutual-user\" string key", auth_group->ag_name); return (1); } mutual_secret = ucl_object_find_key(obj, "mutual-secret"); if (!secret || secret->type != UCL_STRING) { log_warnx("chap-mutual section in auth-group \"%s\" is missing " "\"mutual-secret\" string key", auth_group->ag_name); return (1); } ca = auth_new_chap_mutual(auth_group, ucl_object_tostring(user), ucl_object_tostring(secret), ucl_object_tostring(mutual_user), ucl_object_tostring(mutual_secret)); if (ca == NULL) return (1); return (0); } static int uclparse_target_portal_group(struct target *target, const ucl_object_t *obj) { struct portal_group *tpg; struct auth_group *tag = NULL; struct port *tp; const ucl_object_t *portal_group, *auth_group; portal_group = ucl_object_find_key(obj, "name"); if (!portal_group || portal_group->type != UCL_STRING) { log_warnx("portal-group section in target \"%s\" is missing " "\"name\" string key", target->t_name); return (1); } auth_group = ucl_object_find_key(obj, "auth-group-name"); if (auth_group && auth_group->type != UCL_STRING) { log_warnx("portal-group section in target \"%s\" is missing " "\"auth-group-name\" string key", target->t_name); return (1); } tpg = portal_group_find(conf, ucl_object_tostring(portal_group)); if (tpg == NULL) { log_warnx("unknown portal-group \"%s\" for target " "\"%s\"", ucl_object_tostring(portal_group), target->t_name); return (1); } if (auth_group) { tag = auth_group_find(conf, ucl_object_tostring(auth_group)); if (tag == NULL) { log_warnx("unknown auth-group \"%s\" for target " "\"%s\"", ucl_object_tostring(auth_group), target->t_name); return (1); } } tp = port_new(conf, target, tpg); if (tp == NULL) { log_warnx("can't link portal-group \"%s\" to target " "\"%s\"", ucl_object_tostring(portal_group), target->t_name); return (1); } tp->p_auth_group = tag; return (0); } static int uclparse_target_lun(struct target *target, const ucl_object_t *obj) { struct lun *lun; uint64_t tmp; if (obj->type == UCL_INT) { char *name; tmp = ucl_object_toint(obj); if (tmp >= MAX_LUNS) { log_warnx("LU number %ju in target \"%s\" is too big", tmp, target->t_name); 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; return (0); } if (obj->type == UCL_OBJECT) { const ucl_object_t *num = ucl_object_find_key(obj, "number"); const ucl_object_t *name = ucl_object_find_key(obj, "name"); if (num == NULL || num->type != UCL_INT) { log_warnx("lun section in target \"%s\" is missing " "\"number\" integer property", target->t_name); return (1); } tmp = ucl_object_toint(num); if (tmp >= MAX_LUNS) { log_warnx("LU number %ju in target \"%s\" is too big", tmp, target->t_name); return (1); } if (name == NULL || name->type != UCL_STRING) { log_warnx("lun section in target \"%s\" is missing " "\"name\" string property", target->t_name); return (1); } lun = lun_find(conf, ucl_object_tostring(name)); if (lun == NULL) return (1); target->t_luns[tmp] = lun; } return (0); } static int uclparse_toplevel(const ucl_object_t *top) { ucl_object_iter_t it = NULL, iter = NULL; const ucl_object_t *obj = NULL, *child = NULL; int err = 0; /* Pass 1 - everything except targets */ while ((obj = ucl_iterate_object(top, &it, true))) { const char *key = ucl_object_key(obj); if (!strcmp(key, "debug")) { if (obj->type == UCL_INT) conf->conf_debug = ucl_object_toint(obj); else { log_warnx("\"debug\" property value is not integer"); return (1); } } if (!strcmp(key, "timeout")) { if (obj->type == UCL_INT) conf->conf_timeout = ucl_object_toint(obj); else { log_warnx("\"timeout\" property value is not integer"); return (1); } } if (!strcmp(key, "maxproc")) { if (obj->type == UCL_INT) conf->conf_maxproc = ucl_object_toint(obj); else { log_warnx("\"maxproc\" property value is not integer"); return (1); } } if (!strcmp(key, "pidfile")) { if (obj->type == UCL_STRING) conf->conf_pidfile_path = strdup( ucl_object_tostring(obj)); else { log_warnx("\"pidfile\" property value is not string"); return (1); } } if (!strcmp(key, "isns-server")) { if (obj->type == UCL_ARRAY) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { if (child->type != UCL_STRING) return (1); err = isns_new(conf, ucl_object_tostring(child)); if (err != 0) { return (1); } } } else { log_warnx("\"isns-server\" property value is " "not an array"); return (1); } } if (!strcmp(key, "isns-period")) { if (obj->type == UCL_INT) conf->conf_timeout = ucl_object_toint(obj); else { log_warnx("\"isns-period\" property value is not integer"); return (1); } } if (!strcmp(key, "isns-timeout")) { if (obj->type == UCL_INT) conf->conf_timeout = ucl_object_toint(obj); else { log_warnx("\"isns-timeout\" property value is not integer"); return (1); } } if (!strcmp(key, "auth-group")) { if (obj->type == UCL_OBJECT) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { uclparse_auth_group(ucl_object_key(child), child); } } else { log_warnx("\"auth-group\" section is not an object"); return (1); } } if (!strcmp(key, "portal-group")) { if (obj->type == UCL_OBJECT) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { uclparse_portal_group(ucl_object_key(child), child); } } else { log_warnx("\"portal-group\" section is not an object"); return (1); } } if (!strcmp(key, "lun")) { if (obj->type == UCL_OBJECT) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { uclparse_lun(ucl_object_key(child), child); } } else { log_warnx("\"lun\" section is not an object"); return (1); } } } /* Pass 2 - targets */ it = NULL; while ((obj = ucl_iterate_object(top, &it, true))) { const char *key = ucl_object_key(obj); if (!strcmp(key, "target")) { if (obj->type == UCL_OBJECT) { iter = NULL; while ((child = ucl_iterate_object(obj, &iter, true))) { uclparse_target(ucl_object_key(child), child); } } else { log_warnx("\"target\" section is not an object"); return (1); } } } return (0); } static int uclparse_auth_group(const char *name, const ucl_object_t *top) { struct auth_group *auth_group; const struct auth_name *an; const struct auth_portal *ap; ucl_object_iter_t it = NULL, it2 = NULL; const ucl_object_t *obj = NULL, *tmp = NULL; const char *key; int err; if (!strcmp(name, "default") && conf->conf_default_ag_defined == false) { auth_group = auth_group_find(conf, name); conf->conf_default_ag_defined = true; } else { auth_group = auth_group_new(conf, name); } if (auth_group == NULL) return (1); while ((obj = ucl_iterate_object(top, &it, true))) { key = ucl_object_key(obj); if (!strcmp(key, "auth-type")) { const char *value = ucl_object_tostring(obj); err = auth_group_set_type(auth_group, value); if (err) return (1); } if (!strcmp(key, "chap")) { if (obj->type != UCL_ARRAY) { log_warnx("\"chap\" property of " "auth-group \"%s\" is not an array", name); return (1); } it2 = NULL; while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (uclparse_chap(auth_group, tmp) != 0) return (1); } } if (!strcmp(key, "chap-mutual")) { if (obj->type != UCL_ARRAY) { log_warnx("\"chap-mutual\" property of " "auth-group \"%s\" is not an array", name); return (1); } it2 = NULL; while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (uclparse_chap_mutual(auth_group, tmp) != 0) return (1); } } if (!strcmp(key, "initiator-name")) { if (obj->type != UCL_ARRAY) { log_warnx("\"initiator-name\" property of " "auth-group \"%s\" is not an array", name); return (1); } it2 = NULL; while ((tmp = ucl_iterate_object(obj, &it2, true))) { const char *value = ucl_object_tostring(tmp); an = auth_name_new(auth_group, value); if (an == NULL) return (1); } } if (!strcmp(key, "initiator-portal")) { if (obj->type != UCL_ARRAY) { log_warnx("\"initiator-portal\" property of " "auth-group \"%s\" is not an array", name); return (1); } it2 = NULL; while ((tmp = ucl_iterate_object(obj, &it2, true))) { const char *value = ucl_object_tostring(tmp); ap = auth_portal_new(auth_group, value); if (ap == NULL) return (1); } } } return (0); } static int uclparse_portal_group(const char *name, const ucl_object_t *top) { struct portal_group *portal_group; ucl_object_iter_t it = NULL, it2 = NULL; const ucl_object_t *obj = NULL, *tmp = NULL; const char *key; if (strcmp(name, "default") == 0 && conf->conf_default_pg_defined == false) { portal_group = portal_group_find(conf, name); conf->conf_default_pg_defined = true; } else { portal_group = portal_group_new(conf, name); } if (portal_group == NULL) return (1); while ((obj = ucl_iterate_object(top, &it, true))) { key = ucl_object_key(obj); if (!strcmp(key, "discovery-auth-group")) { portal_group->pg_discovery_auth_group = auth_group_find(conf, ucl_object_tostring(obj)); if (portal_group->pg_discovery_auth_group == NULL) { log_warnx("unknown discovery-auth-group \"%s\" " "for portal-group \"%s\"", ucl_object_tostring(obj), portal_group->pg_name); return (1); } } if (!strcmp(key, "discovery-filter")) { if (obj->type != UCL_STRING) { log_warnx("\"discovery-filter\" property of " "portal-group \"%s\" is not a string", portal_group->pg_name); return (1); } if (portal_group_set_filter(portal_group, ucl_object_tostring(obj)) != 0) return (1); } if (!strcmp(key, "listen")) { if (obj->type == UCL_STRING) { if (portal_group_add_listen(portal_group, ucl_object_tostring(obj), false) != 0) return (1); } else if (obj->type == UCL_ARRAY) { while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (portal_group_add_listen( portal_group, ucl_object_tostring(tmp), false) != 0) return (1); } } else { log_warnx("\"listen\" property of " "portal-group \"%s\" is not a string", portal_group->pg_name); return (1); } } if (!strcmp(key, "listen-iser")) { if (obj->type == UCL_STRING) { if (portal_group_add_listen(portal_group, ucl_object_tostring(obj), true) != 0) return (1); } else if (obj->type == UCL_ARRAY) { while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (portal_group_add_listen( portal_group, ucl_object_tostring(tmp), true) != 0) return (1); } } else { log_warnx("\"listen\" property of " "portal-group \"%s\" is not a string", portal_group->pg_name); return (1); } } if (!strcmp(key, "redirect")) { if (obj->type != UCL_STRING) { log_warnx("\"listen\" property of " "portal-group \"%s\" is not a string", portal_group->pg_name); return (1); } if (portal_group_set_redirection(portal_group, ucl_object_tostring(obj)) != 0) return (1); } if (!strcmp(key, "options")) { if (obj->type != UCL_OBJECT) { log_warnx("\"options\" property of portal group " "\"%s\" is not an object", portal_group->pg_name); return (1); } while ((tmp = ucl_iterate_object(obj, &it2, true))) { option_new(&portal_group->pg_options, ucl_object_key(tmp), ucl_object_tostring_forced(tmp)); } } - } + + if (!strcmp(key, "dscp")) { + if ((obj->type != UCL_STRING) && (obj->type != UCL_INT)) { + log_warnx("\"dscp\" property of portal group " + "\"%s\" is not a string or integer", portal_group->pg_name); + return(1); + } + if (obj->type == UCL_INT) + portal_group->pg_dscp = ucl_object_toint(obj); + else { + key = ucl_object_tostring(obj); + if (strcmp(key, "0x") == 0) + portal_group->pg_dscp = strtol(key + 2, NULL, 16); + else if (strcmp(key, "be") || strcmp(key, "cs0")) + portal_group->pg_dscp = IPTOS_DSCP_CS0 >> 2; + else if (strcmp(key, "ef")) + portal_group->pg_dscp = IPTOS_DSCP_EF >> 2; + else if (strcmp(key, "cs0")) + portal_group->pg_dscp = IPTOS_DSCP_CS0 >> 2; + else if (strcmp(key, "cs1")) + portal_group->pg_dscp = IPTOS_DSCP_CS1 >> 2; + else if (strcmp(key, "cs2")) + portal_group->pg_dscp = IPTOS_DSCP_CS2 >> 2; + else if (strcmp(key, "cs3")) + portal_group->pg_dscp = IPTOS_DSCP_CS3 >> 2; + else if (strcmp(key, "cs4")) + portal_group->pg_dscp = IPTOS_DSCP_CS4 >> 2; + else if (strcmp(key, "cs5")) + portal_group->pg_dscp = IPTOS_DSCP_CS5 >> 2; + else if (strcmp(key, "cs6")) + portal_group->pg_dscp = IPTOS_DSCP_CS6 >> 2; + else if (strcmp(key, "cs7")) + portal_group->pg_dscp = IPTOS_DSCP_CS7 >> 2; + else if (strcmp(key, "af11")) + portal_group->pg_dscp = IPTOS_DSCP_AF11 >> 2; + else if (strcmp(key, "af12")) + portal_group->pg_dscp = IPTOS_DSCP_AF12 >> 2; + else if (strcmp(key, "af13")) + portal_group->pg_dscp = IPTOS_DSCP_AF13 >> 2; + else if (strcmp(key, "af21")) + portal_group->pg_dscp = IPTOS_DSCP_AF21 >> 2; + else if (strcmp(key, "af22")) + portal_group->pg_dscp = IPTOS_DSCP_AF22 >> 2; + else if (strcmp(key, "af23")) + portal_group->pg_dscp = IPTOS_DSCP_AF23 >> 2; + else if (strcmp(key, "af31")) + portal_group->pg_dscp = IPTOS_DSCP_AF31 >> 2; + else if (strcmp(key, "af32")) + portal_group->pg_dscp = IPTOS_DSCP_AF32 >> 2; + else if (strcmp(key, "af33")) + portal_group->pg_dscp = IPTOS_DSCP_AF33 >> 2; + else if (strcmp(key, "af41")) + portal_group->pg_dscp = IPTOS_DSCP_AF41 >> 2; + else if (strcmp(key, "af42")) + portal_group->pg_dscp = IPTOS_DSCP_AF42 >> 2; + else if (strcmp(key, "af43")) + portal_group->pg_dscp = IPTOS_DSCP_AF43 >> 2; + else { + log_warnx("\"dscp\" property value is not a supported textual value"); + return (1); + } + } + } + } return (0); } static int uclparse_target(const char *name, const ucl_object_t *top) { struct target *target; ucl_object_iter_t it = NULL, it2 = NULL; const ucl_object_t *obj = NULL, *tmp = NULL; const char *key; target = target_new(conf, name); if (target == NULL) return (1); while ((obj = ucl_iterate_object(top, &it, true))) { key = ucl_object_key(obj); if (!strcmp(key, "alias")) { if (obj->type != UCL_STRING) { log_warnx("\"alias\" property of target " "\"%s\" is not a string", target->t_name); return (1); } target->t_alias = strdup(ucl_object_tostring(obj)); } if (!strcmp(key, "auth-group")) { 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, ucl_object_tostring(obj)); if (target->t_auth_group == NULL) { log_warnx("unknown auth-group \"%s\" for target " "\"%s\"", ucl_object_tostring(obj), target->t_name); return (1); } } if (!strcmp(key, "auth-type")) { 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) return (1); target->t_auth_group->ag_target = target; } error = auth_group_set_type(target->t_auth_group, ucl_object_tostring(obj)); if (error != 0) return (1); } if (!strcmp(key, "chap")) { if (uclparse_chap(target->t_auth_group, obj) != 0) return (1); } if (!strcmp(key, "chap-mutual")) { if (uclparse_chap_mutual(target->t_auth_group, obj) != 0) return (1); } if (!strcmp(key, "initiator-name")) { 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); return (1); } } else { target->t_auth_group = auth_group_new(conf, NULL); if (target->t_auth_group == NULL) return (1); target->t_auth_group->ag_target = target; } an = auth_name_new(target->t_auth_group, ucl_object_tostring(obj)); if (an == NULL) return (1); } if (!strcmp(key, "initiator-portal")) { 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); return (1); } } else { target->t_auth_group = auth_group_new(conf, NULL); if (target->t_auth_group == NULL) return (1); target->t_auth_group->ag_target = target; } ap = auth_portal_new(target->t_auth_group, ucl_object_tostring(obj)); if (ap == NULL) return (1); } if (!strcmp(key, "portal-group")) { if (obj->type == UCL_OBJECT) { if (uclparse_target_portal_group(target, obj) != 0) return (1); } if (obj->type == UCL_ARRAY) { while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (uclparse_target_portal_group(target, tmp) != 0) return (1); } } } if (!strcmp(key, "port")) { struct pport *pp; struct port *tp; const char *value = ucl_object_tostring(obj); int ret, i_pp, i_vp = 0; ret = sscanf(value, "ioctl/%d/%d", &i_pp, &i_vp); if (ret > 0) { tp = port_new_ioctl(conf, target, i_pp, i_vp); if (tp == NULL) { log_warnx("can't create new ioctl port " "for target \"%s\"", target->t_name); return (1); } return (0); } pp = pport_find(conf, value); if (pp == NULL) { log_warnx("unknown port \"%s\" for target \"%s\"", value, target->t_name); return (1); } if (!TAILQ_EMPTY(&pp->pp_ports)) { log_warnx("can't link port \"%s\" to target \"%s\", " "port already linked to some target", value, target->t_name); return (1); } tp = port_new_pp(conf, target, pp); if (tp == NULL) { log_warnx("can't link port \"%s\" to target \"%s\"", value, target->t_name); return (1); } } if (!strcmp(key, "redirect")) { if (obj->type != UCL_STRING) { log_warnx("\"redirect\" property of target " "\"%s\" is not a string", target->t_name); return (1); } if (target_set_redirection(target, ucl_object_tostring(obj)) != 0) return (1); } if (!strcmp(key, "lun")) { while ((tmp = ucl_iterate_object(obj, &it2, true))) { if (uclparse_target_lun(target, tmp) != 0) return (1); } } } return (0); } static int uclparse_lun(const char *name, const ucl_object_t *top) { struct lun *lun; ucl_object_iter_t it = NULL, child_it = NULL; const ucl_object_t *obj = NULL, *child = NULL; const char *key; lun = lun_new(conf, name); if (lun == NULL) return (1); while ((obj = ucl_iterate_object(top, &it, true))) { key = ucl_object_key(obj); if (!strcmp(key, "backend")) { if (obj->type != UCL_STRING) { log_warnx("\"backend\" property of lun " "\"%s\" is not a string", lun->l_name); return (1); } lun_set_backend(lun, ucl_object_tostring(obj)); } if (!strcmp(key, "blocksize")) { if (obj->type != UCL_INT) { log_warnx("\"blocksize\" property of lun " "\"%s\" is not an integer", lun->l_name); return (1); } lun_set_blocksize(lun, ucl_object_toint(obj)); } if (!strcmp(key, "device-id")) { if (obj->type != UCL_STRING) { log_warnx("\"device-id\" property of lun " "\"%s\" is not an integer", lun->l_name); return (1); } lun_set_device_id(lun, ucl_object_tostring(obj)); } if (!strcmp(key, "options")) { if (obj->type != UCL_OBJECT) { log_warnx("\"options\" property of lun " "\"%s\" is not an object", lun->l_name); return (1); } while ((child = ucl_iterate_object(obj, &child_it, true))) { option_new(&lun->l_options, ucl_object_key(child), ucl_object_tostring_forced(child)); } } if (!strcmp(key, "path")) { if (obj->type != UCL_STRING) { log_warnx("\"path\" property of lun " "\"%s\" is not a string", lun->l_name); return (1); } lun_set_path(lun, ucl_object_tostring(obj)); } if (!strcmp(key, "serial")) { if (obj->type != UCL_STRING) { log_warnx("\"serial\" property of lun " "\"%s\" is not a string", lun->l_name); return (1); } lun_set_serial(lun, ucl_object_tostring(obj)); } if (!strcmp(key, "size")) { if (obj->type != UCL_INT) { log_warnx("\"size\" property of lun " "\"%s\" is not an integer", lun->l_name); return (1); } lun_set_size(lun, ucl_object_toint(obj)); } } return (0); } int uclparse_conf(struct conf *newconf, const char *path) { struct ucl_parser *parser; ucl_object_t *top; int error; conf = newconf; parser = ucl_parser_new(0); if (!ucl_parser_add_file(parser, path)) { log_warn("unable to parse configuration file %s: %s", path, ucl_parser_get_error(parser)); ucl_parser_free(parser); return (1); } top = ucl_parser_get_object(parser); error = uclparse_toplevel(top); ucl_object_unref(top); ucl_parser_free(parser); return (error); } Index: stable/12 =================================================================== --- stable/12 (revision 366626) +++ stable/12 (revision 366627) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r366206