Index: head/sys/dev/iscsi/iscsi_ioctl.h =================================================================== --- head/sys/dev/iscsi/iscsi_ioctl.h (revision 367022) +++ head/sys/dev/iscsi/iscsi_ioctl.h (revision 367023) @@ -1,240 +1,241 @@ /*- * 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 ISCSI_IOCTL_H #define ISCSI_IOCTL_H #ifdef ICL_KERNEL_PROXY #include #endif #define ISCSI_PATH "/dev/iscsi" #define ISCSI_MAX_DATA_SEGMENT_LENGTH (128 * 1024) #define ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */ #define ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */ #define ISCSI_ALIAS_LEN 256 /* XXX: Where did it come from? */ #define ISCSI_SECRET_LEN 17 /* 16 + '\0' */ #define ISCSI_OFFLOAD_LEN 8 #define ISCSI_REASON_LEN 64 #define ISCSI_DIGEST_NONE 0 #define ISCSI_DIGEST_CRC32C 1 /* * Session configuration, set when adding the session. */ struct iscsi_session_conf { char isc_initiator[ISCSI_NAME_LEN]; char isc_initiator_addr[ISCSI_ADDR_LEN]; char isc_initiator_alias[ISCSI_ALIAS_LEN]; char isc_target[ISCSI_NAME_LEN]; char isc_target_addr[ISCSI_ADDR_LEN]; char isc_user[ISCSI_NAME_LEN]; char isc_secret[ISCSI_SECRET_LEN]; char isc_mutual_user[ISCSI_NAME_LEN]; char isc_mutual_secret[ISCSI_SECRET_LEN]; int isc_discovery; int isc_header_digest; int isc_data_digest; int isc_iser; char isc_offload[ISCSI_OFFLOAD_LEN]; int isc_enable; int isc_dscp; - int isc_spare[3]; + int isc_pcp; + int isc_spare[2]; }; /* * Additional constraints imposed by chosen ICL offload module; * iscsid(8) must obey those when negotiating operational parameters. */ struct iscsi_session_limits { size_t isl_spare0; int isl_max_recv_data_segment_length; int isl_max_send_data_segment_length; int isl_max_burst_length; int isl_first_burst_length; int isl_spare[4]; }; /* * Session state, negotiated by iscsid(8) and queried by iscsictl(8). */ struct iscsi_session_state { struct iscsi_session_conf iss_conf; unsigned int iss_id; char iss_target_alias[ISCSI_ALIAS_LEN]; int iss_header_digest; int iss_data_digest; int iss_max_recv_data_segment_length; int iss_max_burst_length; int iss_first_burst_length; int iss_immediate_data; int iss_connected; char iss_reason[ISCSI_REASON_LEN]; char iss_offload[ISCSI_OFFLOAD_LEN]; int iss_max_send_data_segment_length; int iss_spare[3]; }; /* * The following ioctls are used by iscsid(8). */ struct iscsi_daemon_request { unsigned int idr_session_id; struct iscsi_session_conf idr_conf; uint8_t idr_isid[6]; uint16_t idr_tsih; uint16_t idr_spare_cid; struct iscsi_session_limits idr_limits; int idr_spare[4]; }; struct iscsi_daemon_handoff { unsigned int idh_session_id; int idh_socket; char idh_target_alias[ISCSI_ALIAS_LEN]; int idh_protocol_level; uint16_t idh_spare; uint16_t idh_tsih; uint16_t idh_spare_cid; uint32_t idh_statsn; int idh_header_digest; int idh_data_digest; size_t spare[3]; int idh_immediate_data; int idh_initial_r2t; int idh_max_recv_data_segment_length; int idh_max_send_data_segment_length; int idh_max_burst_length; int idh_first_burst_length; }; struct iscsi_daemon_fail { unsigned int idf_session_id; char idf_reason[ISCSI_REASON_LEN]; int idf_spare[4]; }; #define ISCSIDWAIT _IOR('I', 0x01, struct iscsi_daemon_request) #define ISCSIDHANDOFF _IOW('I', 0x02, struct iscsi_daemon_handoff) #define ISCSIDFAIL _IOW('I', 0x03, struct iscsi_daemon_fail) #ifdef ICL_KERNEL_PROXY /* * When ICL_KERNEL_PROXY is not defined, the iscsid(8) is responsible * for creating the socket, connecting, and performing Login Phase using * the socket in the usual userspace way, and then passing the socket * file descriptor to the kernel part using ISCSIDHANDOFF. * * When ICL_KERNEL_PROXY is defined, the iscsid(8) creates the session * using ISCSICONNECT, performs Login Phase using ISCSISEND/ISCSIRECEIVE * instead of read(2)/write(2), and then calls ISCSIDHANDOFF with * idh_socket set to 0. * * The purpose of ICL_KERNEL_PROXY is to workaround the fact that, * at this time, it's not possible to do iWARP (RDMA) in userspace. */ struct iscsi_daemon_connect { unsigned int idc_session_id; int idc_iser; int idc_domain; int idc_socktype; int idc_protocol; struct sockaddr *idc_from_addr; socklen_t idc_from_addrlen; struct sockaddr *idc_to_addr; socklen_t idc_to_addrlen; int idc_spare[4]; }; struct iscsi_daemon_send { unsigned int ids_session_id; void *ids_bhs; size_t ids_spare; void *ids_spare2; size_t ids_data_segment_len; void *ids_data_segment; int ids_spare3[4]; }; struct iscsi_daemon_receive { unsigned int idr_session_id; void *idr_bhs; size_t idr_spare; void *idr_spare2; size_t idr_data_segment_len; void *idr_data_segment; int idr_spare3[4]; }; #define ISCSIDCONNECT _IOWR('I', 0x04, struct iscsi_daemon_connect) #define ISCSIDSEND _IOWR('I', 0x05, struct iscsi_daemon_send) #define ISCSIDRECEIVE _IOWR('I', 0x06, struct iscsi_daemon_receive) #endif /* ICL_KERNEL_PROXY */ /* * The following ioctls are used by iscsictl(8). */ struct iscsi_session_add { struct iscsi_session_conf isa_conf; int isa_spare[4]; }; struct iscsi_session_remove { unsigned int isr_session_id; struct iscsi_session_conf isr_conf; int isr_spare[4]; }; struct iscsi_session_list { unsigned int isl_nentries; struct iscsi_session_state *isl_pstates; int isl_spare[4]; }; struct iscsi_session_modify { unsigned int ism_session_id; struct iscsi_session_conf ism_conf; int ism_spare[4]; }; #define ISCSISADD _IOW('I', 0x11, struct iscsi_session_add) #define ISCSISREMOVE _IOW('I', 0x12, struct iscsi_session_remove) #define ISCSISLIST _IOWR('I', 0x13, struct iscsi_session_list) #define ISCSISMODIFY _IOWR('I', 0x14, struct iscsi_session_modify) #endif /* !ISCSI_IOCTL_H */ Index: head/usr.bin/iscsictl/iscsi.conf.5 =================================================================== --- head/usr.bin/iscsictl/iscsi.conf.5 (revision 367022) +++ head/usr.bin/iscsictl/iscsi.conf.5 (revision 367023) @@ -1,188 +1,195 @@ .\" Copyright (c) 2007-2010 Daniel Braniss .\" All rights reserved. .\" .\" 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$ .\" .Dd May 6, 2016 .Dt ISCSI.CONF 5 .Os .Sh NAME .Nm iscsi.conf .Nd iSCSI initiator configuration file .Sh DESCRIPTION The .Nm configuration file is used by the .Xr iscsictl 8 and .Xr iscontrol 8 utilities. The general syntax is: .Bf Li .Bd -literal # this is a comment nickname_1 { variable = value; ... } nickname_2 { variable = value; ... } ... .Ed .Ef .Bl -tag -width MaxConnections .It Cm AuthMethod Sets the authentication type. Type can be either .Qq Ar None , or .Qq Ar CHAP . Default is .Qq Ar None . When set to .Cm CHAP , both .Cm chapIName and .Cm chapSecret must be defined. .It Cm chapIName Login for CHAP authentication. .It Cm chapSecret Secret for CHAP authentication. .It Cm tgtChapName Target login for Mutual CHAP authentication. .It Cm tgtChapSecret Target secret for Mutual CHAP authentication. .It Cm HeaderDigest Sets the header digest; a checksum calculated over the header of iSCSI PDUs, and verified on receive. Digest can be either .Qq Ar None , or .Qq Ar CRC32C . Default is .Qq Ar None . .It Cm DataDigest Sets the data digest; a checksum calculated over the Data Section of iSCSI PDUs, and verified on receive. Digest can be either .Qq Ar None , or .Qq Ar CRC32C . Default is .Qq Ar None . .It Cm InitiatorName Sets the initiator name. By default, the name is concatenation of .Qq Ar iqn.1994-09.org.freebsd: with the hostname. .It Cm TargetName Sets the target name. Not required for discovery sessions. .It Cm TargetAddress Sets the target address and port, in .Sy address[:port] format. The .Sy address can be either an IP address, or hostname. The optional port defaults to 3260. .It Cm SessionType Sets the session type. Type can be either .Qq Ar Discovery , or .Qq Ar Normal . Default is .Qq Ar Normal . For normal sessions, the .Sy TargetName must be defined. Discovery sessions result in the initiator connecting to all the targets returned by SendTargets iSCSI discovery with the defined .Sy TargetAddress . .It Cm Enable Enable or disable the session. State can be either .Qq Ar On , or .Qq Ar Off . Default is .Qq Ar On . .It Cm Offload Name of selected iSCSI hardware offload driver. Default is .Qq Ar None . .It Cm Protocol Name of selected protocol. It can be either .Qq Ar iSER , for iSCSI over RDMA, or .Qq Ar iSCSI . Default is .Qq Ar iSCSI . .It Cm dscp 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 cs and .Qq Ar af codepoints. Default is no specified dscp codepoint, which means the default of the outgoing interface is used. +.It Cm pcp +The 802.1Q Priority CodePoint used for sending packets. +The PCP can be set to a value in the range between +.Qq Ar 0 +to +.Qq Ar 7 . +When omitted, the default for the outgoing interface is used. .El .Sh FILES .Bl -tag -width indent .It Pa /etc/iscsi.conf .El .Sh EXAMPLES .Bd -literal myiscsi { # nickname targetaddress = iscsi1 targetname = iqn.1900.com.com:sn.123456 } myiscsi6 { # nickname targetaddress = [2001:db8::de:ef]:3260 targetname = iqn.1900.com.com:sn.123456 } chaptest { targetaddress = 10.0.0.1; targetname = iqn.1900.com.com:sn.123456; initiatorname = iqn.2005-01.il.ac.huji.cs:nobody; authmethod = CHAP; chapiname = iqn.2005-01.il.ac.huji.cs:nobody; chapsecret = "secretsecret"; } .Ed .Sh SEE ALSO .Xr iscontrol 8 , .Xr iscsictl 8 .\"Sh HISTORY .\"Sh AUTHORS Index: head/usr.bin/iscsictl/iscsictl.c =================================================================== --- head/usr.bin/iscsictl/iscsictl.c (revision 367022) +++ head/usr.bin/iscsictl/iscsictl.c (revision 367023) @@ -1,1043 +1,1048 @@ /*- * 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 "iscsictl.h" struct conf * conf_new(void) { struct conf *conf; conf = calloc(1, sizeof(*conf)); if (conf == NULL) xo_err(1, "calloc"); TAILQ_INIT(&conf->conf_targets); return (conf); } struct target * target_find(struct conf *conf, const char *nickname) { struct target *targ; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_nickname != NULL && strcasecmp(targ->t_nickname, nickname) == 0) return (targ); } return (NULL); } struct target * target_new(struct conf *conf) { struct target *targ; targ = calloc(1, sizeof(*targ)); if (targ == NULL) xo_err(1, "calloc"); targ->t_conf = conf; targ->t_dscp = -1; + targ->t_pcp = -1; TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); return (targ); } void target_delete(struct target *targ) { TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); free(targ); } static char * default_initiator_name(void) { char *name; size_t namelen; int error; namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN); name = calloc(1, namelen + 1); if (name == NULL) xo_err(1, "calloc"); strcpy(name, DEFAULT_IQN); error = gethostname(name + strlen(DEFAULT_IQN), namelen - strlen(DEFAULT_IQN)); if (error != 0) xo_err(1, "gethostname"); return (name); } 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); } } int parse_enable(const char *enable) { if (enable == NULL) return (ENABLE_UNSPECIFIED); if (strcasecmp(enable, "on") == 0 || strcasecmp(enable, "yes") == 0) return (ENABLE_ON); if (strcasecmp(enable, "off") == 0 || strcasecmp(enable, "no") == 0) return (ENABLE_OFF); return (ENABLE_UNSPECIFIED); } bool valid_iscsi_name(const char *name) { int i; if (strlen(name) >= MAX_NAME_LEN) { xo_warnx("overlong name for \"%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; xo_warnx("invalid character \"%c\" in iSCSI 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) xo_warnx("invalid iSCSI 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])) { xo_warnx("invalid character \"%c\" in iSCSI " "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) xo_warnx("invalid iSCSI 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])) { xo_warnx("invalid character \"%c\" in ISCSI " "name \"%s\"; allowed characters are 1-9 " "and A-F", name[i], name); break; } } } else { xo_warnx("invalid iSCSI name \"%s\"; should start with " "either \".iqn\", \"eui.\", or \"naa.\"", name); } return (true); } void conf_verify(struct conf *conf) { struct target *targ; TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { assert(targ->t_nickname != NULL); if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED) targ->t_session_type = SESSION_TYPE_NORMAL; if (targ->t_session_type == SESSION_TYPE_NORMAL && targ->t_name == NULL) xo_errx(1, "missing TargetName for target \"%s\"", targ->t_nickname); if (targ->t_session_type == SESSION_TYPE_DISCOVERY && targ->t_name != NULL) xo_errx(1, "cannot specify TargetName for discovery " "sessions for target \"%s\"", targ->t_nickname); if (targ->t_name != NULL) { if (valid_iscsi_name(targ->t_name) == false) xo_errx(1, "invalid target name \"%s\"", targ->t_name); } if (targ->t_protocol == PROTOCOL_UNSPECIFIED) targ->t_protocol = PROTOCOL_ISCSI; if (targ->t_address == NULL) xo_errx(1, "missing TargetAddress for target \"%s\"", targ->t_nickname); if (targ->t_initiator_name == NULL) targ->t_initiator_name = default_initiator_name(); if (valid_iscsi_name(targ->t_initiator_name) == false) xo_errx(1, "invalid initiator name \"%s\"", targ->t_initiator_name); if (targ->t_header_digest == DIGEST_UNSPECIFIED) targ->t_header_digest = DIGEST_NONE; if (targ->t_data_digest == DIGEST_UNSPECIFIED) targ->t_data_digest = DIGEST_NONE; if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) { if (targ->t_user != NULL || targ->t_secret != NULL || targ->t_mutual_user != NULL || targ->t_mutual_secret != NULL) targ->t_auth_method = AUTH_METHOD_CHAP; else targ->t_auth_method = AUTH_METHOD_NONE; } if (targ->t_auth_method == AUTH_METHOD_CHAP) { if (targ->t_user == NULL) { xo_errx(1, "missing chapIName for target \"%s\"", targ->t_nickname); } if (targ->t_secret == NULL) xo_errx(1, "missing chapSecret for target \"%s\"", targ->t_nickname); if (targ->t_mutual_user != NULL || targ->t_mutual_secret != NULL) { if (targ->t_mutual_user == NULL) xo_errx(1, "missing tgtChapName for " "target \"%s\"", targ->t_nickname); if (targ->t_mutual_secret == NULL) xo_errx(1, "missing tgtChapSecret for " "target \"%s\"", targ->t_nickname); } } } } static void conf_from_target(struct iscsi_session_conf *conf, const struct target *targ) { memset(conf, 0, sizeof(*conf)); /* * XXX: Check bounds and return error instead of silently truncating. */ if (targ->t_initiator_name != NULL) strlcpy(conf->isc_initiator, targ->t_initiator_name, sizeof(conf->isc_initiator)); if (targ->t_initiator_address != NULL) strlcpy(conf->isc_initiator_addr, targ->t_initiator_address, sizeof(conf->isc_initiator_addr)); if (targ->t_initiator_alias != NULL) strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias, sizeof(conf->isc_initiator_alias)); if (targ->t_name != NULL) strlcpy(conf->isc_target, targ->t_name, sizeof(conf->isc_target)); if (targ->t_address != NULL) strlcpy(conf->isc_target_addr, targ->t_address, sizeof(conf->isc_target_addr)); if (targ->t_user != NULL) strlcpy(conf->isc_user, targ->t_user, sizeof(conf->isc_user)); if (targ->t_secret != NULL) strlcpy(conf->isc_secret, targ->t_secret, sizeof(conf->isc_secret)); if (targ->t_mutual_user != NULL) strlcpy(conf->isc_mutual_user, targ->t_mutual_user, sizeof(conf->isc_mutual_user)); if (targ->t_mutual_secret != NULL) strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret, sizeof(conf->isc_mutual_secret)); if (targ->t_session_type == SESSION_TYPE_DISCOVERY) conf->isc_discovery = 1; if (targ->t_enable != ENABLE_OFF) conf->isc_enable = 1; if (targ->t_protocol == PROTOCOL_ISER) conf->isc_iser = 1; if (targ->t_offload != NULL) strlcpy(conf->isc_offload, targ->t_offload, sizeof(conf->isc_offload)); if (targ->t_header_digest == DIGEST_CRC32C) conf->isc_header_digest = ISCSI_DIGEST_CRC32C; else conf->isc_header_digest = ISCSI_DIGEST_NONE; if (targ->t_data_digest == DIGEST_CRC32C) conf->isc_data_digest = ISCSI_DIGEST_CRC32C; else conf->isc_data_digest = ISCSI_DIGEST_NONE; conf->isc_dscp = targ->t_dscp; + conf->isc_pcp = targ->t_pcp; } static int kernel_add(int iscsi_fd, const struct target *targ) { struct iscsi_session_add isa; int error; memset(&isa, 0, sizeof(isa)); conf_from_target(&isa.isa_conf, targ); error = ioctl(iscsi_fd, ISCSISADD, &isa); if (error != 0) xo_warn("ISCSISADD"); return (error); } static int kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ) { struct iscsi_session_modify ism; int error; memset(&ism, 0, sizeof(ism)); ism.ism_session_id = session_id; conf_from_target(&ism.ism_conf, targ); error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); if (error != 0) xo_warn("ISCSISMODIFY"); return (error); } static void kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target, const char *target_addr, const char *user, const char *secret, int enable) { struct iscsi_session_state *states = NULL; struct iscsi_session_state *state; struct iscsi_session_conf *conf; struct iscsi_session_list isl; struct iscsi_session_modify ism; unsigned int i, nentries = 1; int error; for (;;) { states = realloc(states, nentries * sizeof(struct iscsi_session_state)); if (states == NULL) xo_err(1, "realloc"); memset(&isl, 0, sizeof(isl)); isl.isl_nentries = nentries; isl.isl_pstates = states; error = ioctl(iscsi_fd, ISCSISLIST, &isl); if (error != 0 && errno == EMSGSIZE) { nentries *= 4; continue; } break; } if (error != 0) xo_errx(1, "ISCSISLIST"); for (i = 0; i < isl.isl_nentries; i++) { state = &states[i]; if (state->iss_id == session_id) break; } if (i == isl.isl_nentries) xo_errx(1, "session-id %u not found", session_id); conf = &state->iss_conf; if (target != NULL) strlcpy(conf->isc_target, target, sizeof(conf->isc_target)); if (target_addr != NULL) strlcpy(conf->isc_target_addr, target_addr, sizeof(conf->isc_target_addr)); if (user != NULL) strlcpy(conf->isc_user, user, sizeof(conf->isc_user)); if (secret != NULL) strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret)); if (enable == ENABLE_ON) conf->isc_enable = 1; else if (enable == ENABLE_OFF) conf->isc_enable = 0; memset(&ism, 0, sizeof(ism)); ism.ism_session_id = session_id; memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf)); error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); if (error != 0) xo_warn("ISCSISMODIFY"); } static int kernel_remove(int iscsi_fd, const struct target *targ) { struct iscsi_session_remove isr; int error; memset(&isr, 0, sizeof(isr)); conf_from_target(&isr.isr_conf, targ); error = ioctl(iscsi_fd, ISCSISREMOVE, &isr); if (error != 0) xo_warn("ISCSISREMOVE"); return (error); } /* * XXX: Add filtering. */ static int kernel_list(int iscsi_fd, const struct target *targ __unused, int verbose) { struct iscsi_session_state *states = NULL; const struct iscsi_session_state *state; const struct iscsi_session_conf *conf; struct iscsi_session_list isl; unsigned int i, nentries = 1; int error; for (;;) { states = realloc(states, nentries * sizeof(struct iscsi_session_state)); if (states == NULL) xo_err(1, "realloc"); memset(&isl, 0, sizeof(isl)); isl.isl_nentries = nentries; isl.isl_pstates = states; error = ioctl(iscsi_fd, ISCSISLIST, &isl); if (error != 0 && errno == EMSGSIZE) { nentries *= 4; continue; } break; } if (error != 0) { xo_warn("ISCSISLIST"); return (error); } if (verbose != 0) { xo_open_list("session"); for (i = 0; i < isl.isl_nentries; i++) { state = &states[i]; conf = &state->iss_conf; xo_open_instance("session"); /* * Display-only modifier as this information * is also present within the 'session' container */ xo_emit("{L:/%-26s}{V:sessionId/%u}\n", "Session ID:", state->iss_id); xo_open_container("initiator"); xo_emit("{L:/%-26s}{V:name/%s}\n", "Initiator name:", conf->isc_initiator); xo_emit("{L:/%-26s}{V:portal/%s}\n", "Initiator portal:", conf->isc_initiator_addr); xo_emit("{L:/%-26s}{V:alias/%s}\n", "Initiator alias:", conf->isc_initiator_alias); xo_close_container("initiator"); xo_open_container("target"); xo_emit("{L:/%-26s}{V:name/%s}\n", "Target name:", conf->isc_target); xo_emit("{L:/%-26s}{V:portal/%s}\n", "Target portal:", conf->isc_target_addr); xo_emit("{L:/%-26s}{V:alias/%s}\n", "Target alias:", state->iss_target_alias); if (conf->isc_dscp != -1) xo_emit("{L:/%-26s}{V:dscp/0x%02x}\n", "Target DSCP:", conf->isc_dscp); + if (conf->isc_pcp != -1) + xo_emit("{L:/%-26s}{V:pcp/0x%02x}\n", + "Target PCP:", conf->isc_pcp); xo_close_container("target"); xo_open_container("auth"); xo_emit("{L:/%-26s}{V:user/%s}\n", "User:", conf->isc_user); xo_emit("{L:/%-26s}{V:secret/%s}\n", "Secret:", conf->isc_secret); xo_emit("{L:/%-26s}{V:mutualUser/%s}\n", "Mutual user:", conf->isc_mutual_user); xo_emit("{L:/%-26s}{V:mutualSecret/%s}\n", "Mutual secret:", conf->isc_mutual_secret); xo_close_container("auth"); xo_emit("{L:/%-26s}{V:type/%s}\n", "Session type:", conf->isc_discovery ? "Discovery" : "Normal"); xo_emit("{L:/%-26s}{V:enable/%s}\n", "Enable:", conf->isc_enable ? "Yes" : "No"); xo_emit("{L:/%-26s}{V:state/%s}\n", "Session state:", state->iss_connected ? "Connected" : "Disconnected"); xo_emit("{L:/%-26s}{V:failureReason/%s}\n", "Failure reason:", state->iss_reason); xo_emit("{L:/%-26s}{V:headerDigest/%s}\n", "Header digest:", state->iss_header_digest == ISCSI_DIGEST_CRC32C ? "CRC32C" : "None"); xo_emit("{L:/%-26s}{V:dataDigest/%s}\n", "Data digest:", state->iss_data_digest == ISCSI_DIGEST_CRC32C ? "CRC32C" : "None"); xo_emit("{L:/%-26s}{V:recvDataSegmentLen/%d}\n", "MaxRecvDataSegmentLength:", state->iss_max_recv_data_segment_length); xo_emit("{L:/%-26s}{V:sendDataSegmentLen/%d}\n", "MaxSendDataSegmentLength:", state->iss_max_send_data_segment_length); xo_emit("{L:/%-26s}{V:maxBurstLen/%d}\n", "MaxBurstLen:", state->iss_max_burst_length); xo_emit("{L:/%-26s}{V:firstBurstLen/%d}\n", "FirstBurstLen:", state->iss_first_burst_length); xo_emit("{L:/%-26s}{V:immediateData/%s}\n", "ImmediateData:", state->iss_immediate_data ? "Yes" : "No"); xo_emit("{L:/%-26s}{V:iSER/%s}\n", "iSER (RDMA):", conf->isc_iser ? "Yes" : "No"); xo_emit("{L:/%-26s}{V:offloadDriver/%s}\n", "Offload driver:", state->iss_offload); xo_emit("{L:/%-26s}", "Device nodes:"); print_periphs(state->iss_id); xo_emit("\n\n"); xo_close_instance("session"); } xo_close_list("session"); } else { xo_emit("{T:/%-36s} {T:/%-16s} {T:/%s}\n", "Target name", "Target portal", "State"); if (isl.isl_nentries != 0) xo_open_list("session"); for (i = 0; i < isl.isl_nentries; i++) { state = &states[i]; conf = &state->iss_conf; xo_open_instance("session"); xo_emit("{V:name/%-36s/%s} {V:portal/%-16s/%s} ", conf->isc_target, conf->isc_target_addr); if (state->iss_reason[0] != '\0' && conf->isc_enable != 0) { xo_emit("{V:state/%s}\n", state->iss_reason); } else { if (conf->isc_discovery) { xo_emit("{V:state}\n", "Discovery"); } else if (conf->isc_enable == 0) { xo_emit("{V:state}\n", "Disabled"); } else if (state->iss_connected) { xo_emit("{V:state}: ", "Connected"); print_periphs(state->iss_id); xo_emit("\n"); } else { xo_emit("{V:state}\n", "Disconnected"); } } xo_close_instance("session"); } if (isl.isl_nentries != 0) xo_close_list("session"); } return (0); } static int kernel_wait(int iscsi_fd, int timeout) { struct iscsi_session_state *states = NULL; const struct iscsi_session_state *state; struct iscsi_session_list isl; unsigned int i, nentries = 1; bool all_connected; int error; for (;;) { for (;;) { states = realloc(states, nentries * sizeof(struct iscsi_session_state)); if (states == NULL) xo_err(1, "realloc"); memset(&isl, 0, sizeof(isl)); isl.isl_nentries = nentries; isl.isl_pstates = states; error = ioctl(iscsi_fd, ISCSISLIST, &isl); if (error != 0 && errno == EMSGSIZE) { nentries *= 4; continue; } break; } if (error != 0) { xo_warn("ISCSISLIST"); return (error); } all_connected = true; for (i = 0; i < isl.isl_nentries; i++) { state = &states[i]; if (!state->iss_connected) { all_connected = false; break; } } if (all_connected) return (0); sleep(1); if (timeout > 0) { timeout--; if (timeout == 0) return (1); } } } static void usage(void) { fprintf(stderr, "usage: iscsictl -A -p portal -t target " "[-u user -s secret] [-w timeout] [-e on | off]\n"); fprintf(stderr, " iscsictl -A -d discovery-host " "[-u user -s secret] [-e on | off]\n"); fprintf(stderr, " iscsictl -A -a [-c path]\n"); fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); fprintf(stderr, " iscsictl -M -i session-id [-p portal] " "[-t target] [-u user] [-s secret] [-e on | off]\n"); fprintf(stderr, " iscsictl -M -i session-id -n nickname " "[-c path]\n"); fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); fprintf(stderr, " iscsictl -R -a\n"); fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); fprintf(stderr, " iscsictl -L [-v] [-w timeout]\n"); exit(1); } int main(int argc, char **argv) { int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, rflag = 0, vflag = 0; const char *conf_path = DEFAULT_CONFIG_PATH; char *nickname = NULL, *discovery_host = NULL, *portal = NULL, *target = NULL, *user = NULL, *secret = NULL; int timeout = -1, enable = ENABLE_UNSPECIFIED; long long session_id = -1; char *end; int ch, error, iscsi_fd, retval, saved_errno; int failed = 0; struct conf *conf; struct target *targ; argc = xo_parse_args(argc, argv); xo_open_container("iscsictl"); while ((ch = getopt(argc, argv, "AMRLac:d:e:i:n:p:rt:u:s:vw:")) != -1) { switch (ch) { case 'A': Aflag = 1; break; case 'M': Mflag = 1; break; case 'R': Rflag = 1; break; case 'L': Lflag = 1; break; case 'a': aflag = 1; break; case 'c': conf_path = optarg; break; case 'd': discovery_host = optarg; break; case 'e': enable = parse_enable(optarg); if (enable == ENABLE_UNSPECIFIED) { xo_errx(1, "invalid argument to -e, " "must be either \"on\" or \"off\""); } break; case 'i': session_id = strtol(optarg, &end, 10); if ((size_t)(end - optarg) != strlen(optarg)) xo_errx(1, "trailing characters after session-id"); if (session_id < 0) xo_errx(1, "session-id cannot be negative"); if (session_id > UINT_MAX) xo_errx(1, "session-id cannot be greater than %u", UINT_MAX); break; case 'n': nickname = optarg; break; case 'p': portal = optarg; break; case 'r': rflag = 1; break; case 't': target = optarg; break; case 'u': user = optarg; break; case 's': secret = optarg; break; case 'v': vflag = 1; break; case 'w': timeout = strtol(optarg, &end, 10); if ((size_t)(end - optarg) != strlen(optarg)) xo_errx(1, "trailing characters after timeout"); if (timeout < 0) xo_errx(1, "timeout cannot be negative"); break; case '?': default: usage(); } } argc -= optind; if (argc != 0) usage(); if (Aflag + Mflag + Rflag + Lflag == 0) Lflag = 1; if (Aflag + Mflag + Rflag + Lflag > 1) xo_errx(1, "at most one of -A, -M, -R, or -L may be specified"); /* * Note that we ignore unnecessary/inapplicable "-c" flag; so that * people can do something like "alias ISCSICTL="iscsictl -c path" * in shell scripts. */ if (Aflag != 0) { if (aflag != 0) { if (enable != ENABLE_UNSPECIFIED) xo_errx(1, "-a and -e are mutually exclusive"); if (portal != NULL) xo_errx(1, "-a and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-a and -t are mutually exclusive"); if (user != NULL) xo_errx(1, "-a and -u are mutually exclusive"); if (secret != NULL) xo_errx(1, "-a and -s are mutually exclusive"); if (nickname != NULL) xo_errx(1, "-a and -n are mutually exclusive"); if (discovery_host != NULL) xo_errx(1, "-a and -d are mutually exclusive"); if (rflag != 0) xo_errx(1, "-a and -r are mutually exclusive"); } else if (nickname != NULL) { if (enable != ENABLE_UNSPECIFIED) xo_errx(1, "-n and -e are mutually exclusive"); if (portal != NULL) xo_errx(1, "-n and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-n and -t are mutually exclusive"); if (user != NULL) xo_errx(1, "-n and -u are mutually exclusive"); if (secret != NULL) xo_errx(1, "-n and -s are mutually exclusive"); if (discovery_host != NULL) xo_errx(1, "-n and -d are mutually exclusive"); if (rflag != 0) xo_errx(1, "-n and -r are mutually exclusive"); } else if (discovery_host != NULL) { if (portal != NULL) xo_errx(1, "-d and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-d and -t are mutually exclusive"); } else { if (target == NULL && portal == NULL) xo_errx(1, "must specify -a, -n or -t/-p"); if (target != NULL && portal == NULL) xo_errx(1, "-t must always be used with -p"); if (portal != NULL && target == NULL) xo_errx(1, "-p must always be used with -t"); } if (user != NULL && secret == NULL) xo_errx(1, "-u must always be used with -s"); if (secret != NULL && user == NULL) xo_errx(1, "-s must always be used with -u"); if (session_id != -1) xo_errx(1, "-i cannot be used with -A"); if (vflag != 0) xo_errx(1, "-v cannot be used with -A"); } else if (Mflag != 0) { if (session_id == -1) xo_errx(1, "-M requires -i"); if (nickname != NULL) { if (enable != ENABLE_UNSPECIFIED) xo_errx(1, "-n and -e are mutually exclusive"); if (portal != NULL) xo_errx(1, "-n and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-n and -t are mutually exclusive"); if (user != NULL) xo_errx(1, "-n and -u are mutually exclusive"); if (secret != NULL) xo_errx(1, "-n and -s are mutually exclusive"); } if (aflag != 0) xo_errx(1, "-a cannot be used with -M"); if (discovery_host != NULL) xo_errx(1, "-d cannot be used with -M"); if (rflag != 0) xo_errx(1, "-r cannot be used with -M"); if (vflag != 0) xo_errx(1, "-v cannot be used with -M"); if (timeout != -1) xo_errx(1, "-w cannot be used with -M"); } else if (Rflag != 0) { if (aflag != 0) { if (portal != NULL) xo_errx(1, "-a and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-a and -t are mutually exclusive"); if (nickname != NULL) xo_errx(1, "-a and -n are mutually exclusive"); } else if (nickname != NULL) { if (portal != NULL) xo_errx(1, "-n and -p are mutually exclusive"); if (target != NULL) xo_errx(1, "-n and -t are mutually exclusive"); } else if (target == NULL && portal == NULL) { xo_errx(1, "must specify either -a, -n, -t, or -p"); } if (discovery_host != NULL) xo_errx(1, "-d cannot be used with -R"); if (enable != ENABLE_UNSPECIFIED) xo_errx(1, "-e cannot be used with -R"); if (session_id != -1) xo_errx(1, "-i cannot be used with -R"); if (rflag != 0) xo_errx(1, "-r cannot be used with -R"); if (user != NULL) xo_errx(1, "-u cannot be used with -R"); if (secret != NULL) xo_errx(1, "-s cannot be used with -R"); if (vflag != 0) xo_errx(1, "-v cannot be used with -R"); if (timeout != -1) xo_errx(1, "-w cannot be used with -R"); } else { assert(Lflag != 0); if (discovery_host != NULL) xo_errx(1, "-d cannot be used with -L"); if (session_id != -1) xo_errx(1, "-i cannot be used with -L"); if (nickname != NULL) xo_errx(1, "-n cannot be used with -L"); if (portal != NULL) xo_errx(1, "-p cannot be used with -L"); if (rflag != 0) xo_errx(1, "-r cannot be used with -L"); if (target != NULL) xo_errx(1, "-t cannot be used with -L"); if (user != NULL) xo_errx(1, "-u cannot be used with -L"); if (secret != NULL) xo_errx(1, "-s cannot be used with -L"); } iscsi_fd = open(ISCSI_PATH, O_RDWR); if (iscsi_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("iscsi"); if (retval != -1) iscsi_fd = open(ISCSI_PATH, O_RDWR); else errno = saved_errno; } if (iscsi_fd < 0) xo_err(1, "failed to open %s", ISCSI_PATH); if (Aflag != 0 && aflag != 0) { conf = conf_new_from_file(conf_path); TAILQ_FOREACH(targ, &conf->conf_targets, t_next) failed += kernel_add(iscsi_fd, targ); } else if (nickname != NULL) { conf = conf_new_from_file(conf_path); targ = target_find(conf, nickname); if (targ == NULL) xo_errx(1, "target %s not found in %s", nickname, conf_path); if (Aflag != 0) failed += kernel_add(iscsi_fd, targ); else if (Mflag != 0) failed += kernel_modify(iscsi_fd, session_id, targ); else if (Rflag != 0) failed += kernel_remove(iscsi_fd, targ); else failed += kernel_list(iscsi_fd, targ, vflag); } else if (Mflag != 0) { kernel_modify_some(iscsi_fd, session_id, target, portal, user, secret, enable); } else { if (Aflag != 0 && target != NULL) { if (valid_iscsi_name(target) == false) xo_errx(1, "invalid target name \"%s\"", target); } conf = conf_new(); targ = target_new(conf); targ->t_initiator_name = default_initiator_name(); targ->t_header_digest = DIGEST_NONE; targ->t_data_digest = DIGEST_NONE; targ->t_name = target; if (discovery_host != NULL) { targ->t_session_type = SESSION_TYPE_DISCOVERY; targ->t_address = discovery_host; } else { targ->t_session_type = SESSION_TYPE_NORMAL; targ->t_address = portal; } targ->t_enable = enable; if (rflag != 0) targ->t_protocol = PROTOCOL_ISER; targ->t_user = user; targ->t_secret = secret; if (Aflag != 0) failed += kernel_add(iscsi_fd, targ); else if (Rflag != 0) failed += kernel_remove(iscsi_fd, targ); else failed += kernel_list(iscsi_fd, targ, vflag); } if (timeout != -1) failed += kernel_wait(iscsi_fd, timeout); error = close(iscsi_fd); if (error != 0) xo_err(1, "close"); xo_close_container("iscsictl"); xo_finish(); if (failed != 0) return (1); return (0); } Index: head/usr.bin/iscsictl/iscsictl.h =================================================================== --- head/usr.bin/iscsictl/iscsictl.h (revision 367022) +++ head/usr.bin/iscsictl/iscsictl.h (revision 367023) @@ -1,107 +1,108 @@ /*- * 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 ISCSICTL_H #define ISCSICTL_H #include #include #include #define DEFAULT_CONFIG_PATH "/etc/iscsi.conf" #define DEFAULT_IQN "iqn.1994-09.org.freebsd:" #define MAX_NAME_LEN 223 #define AUTH_METHOD_UNSPECIFIED 0 #define AUTH_METHOD_NONE 1 #define AUTH_METHOD_CHAP 2 #define DIGEST_UNSPECIFIED 0 #define DIGEST_NONE 1 #define DIGEST_CRC32C 2 #define SESSION_TYPE_UNSPECIFIED 0 #define SESSION_TYPE_NORMAL 1 #define SESSION_TYPE_DISCOVERY 2 #define PROTOCOL_UNSPECIFIED 0 #define PROTOCOL_ISCSI 1 #define PROTOCOL_ISER 2 #define ENABLE_UNSPECIFIED 0 #define ENABLE_ON 1 #define ENABLE_OFF 2 struct target { TAILQ_ENTRY(target) t_next; struct conf *t_conf; char *t_nickname; char *t_name; char *t_address; char *t_initiator_name; char *t_initiator_address; char *t_initiator_alias; int t_header_digest; int t_data_digest; int t_auth_method; int t_session_type; int t_enable; int t_protocol; int t_dscp; + int t_pcp; char *t_offload; char *t_user; char *t_secret; char *t_mutual_user; char *t_mutual_secret; }; struct conf { TAILQ_HEAD(, target) conf_targets; }; struct conf *conf_new(void); struct conf *conf_new_from_file(const char *path); void conf_delete(struct conf *conf); void conf_verify(struct conf *conf); struct target *target_new(struct conf *conf); struct target *target_find(struct conf *conf, const char *nickname); void target_delete(struct target *ic); void print_periphs(int session_id); bool valid_iscsi_name(const char *name); int parse_enable(const char *enable); #endif /* !ISCSICTL_H */ Index: head/usr.bin/iscsictl/parse.y =================================================================== --- head/usr.bin/iscsictl/parse.y (revision 367022) +++ head/usr.bin/iscsictl/parse.y (revision 367023) @@ -1,409 +1,434 @@ %{ /*- * 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 #include #include "iscsictl.h" #include #include extern FILE *yyin; extern char *yytext; extern int lineno; static struct conf *conf; static struct target *target; extern void yyerror(const char *); extern int yylex(void); extern void yyrestart(FILE *); %} %token AUTH_METHOD ENABLE HEADER_DIGEST DATA_DIGEST TARGET_NAME TARGET_ADDRESS %token INITIATOR_NAME INITIATOR_ADDRESS INITIATOR_ALIAS USER SECRET %token MUTUAL_USER MUTUAL_SECRET SEMICOLON SESSION_TYPE PROTOCOL OFFLOAD %token IGNORED EQUALS OPENING_BRACKET CLOSING_BRACKET DSCP %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 %% targets: | targets target ; target: STR OPENING_BRACKET target_entries CLOSING_BRACKET { if (target_find(conf, $1) != NULL) xo_errx(1, "duplicated target %s", $1); target->t_nickname = $1; target = target_new(conf); } ; target_entries: | target_entries target_entry | target_entries target_entry SEMICOLON ; target_entry: target_name | target_address | initiator_name | initiator_address | initiator_alias | user | secret | mutual_user | mutual_secret | auth_method | header_digest | data_digest | session_type | enable | offload | protocol | ignored | dscp + | + pcp ; target_name: TARGET_NAME EQUALS STR { if (target->t_name != NULL) xo_errx(1, "duplicated TargetName at line %d", lineno); target->t_name = $3; } ; target_address: TARGET_ADDRESS EQUALS STR { if (target->t_address != NULL) xo_errx(1, "duplicated TargetAddress at line %d", lineno); target->t_address = $3; } ; initiator_name: INITIATOR_NAME EQUALS STR { if (target->t_initiator_name != NULL) xo_errx(1, "duplicated InitiatorName at line %d", lineno); target->t_initiator_name = $3; } ; initiator_address: INITIATOR_ADDRESS EQUALS STR { if (target->t_initiator_address != NULL) xo_errx(1, "duplicated InitiatorAddress at line %d", lineno); target->t_initiator_address = $3; } ; initiator_alias: INITIATOR_ALIAS EQUALS STR { if (target->t_initiator_alias != NULL) xo_errx(1, "duplicated InitiatorAlias at line %d", lineno); target->t_initiator_alias = $3; } ; user: USER EQUALS STR { if (target->t_user != NULL) xo_errx(1, "duplicated chapIName at line %d", lineno); target->t_user = $3; } ; secret: SECRET EQUALS STR { if (target->t_secret != NULL) xo_errx(1, "duplicated chapSecret at line %d", lineno); target->t_secret = $3; } ; mutual_user: MUTUAL_USER EQUALS STR { if (target->t_mutual_user != NULL) xo_errx(1, "duplicated tgtChapName at line %d", lineno); target->t_mutual_user = $3; } ; mutual_secret: MUTUAL_SECRET EQUALS STR { if (target->t_mutual_secret != NULL) xo_errx(1, "duplicated tgtChapSecret at line %d", lineno); target->t_mutual_secret = $3; } ; auth_method: AUTH_METHOD EQUALS STR { if (target->t_auth_method != AUTH_METHOD_UNSPECIFIED) xo_errx(1, "duplicated AuthMethod at line %d", lineno); if (strcasecmp($3, "none") == 0) target->t_auth_method = AUTH_METHOD_NONE; else if (strcasecmp($3, "chap") == 0) target->t_auth_method = AUTH_METHOD_CHAP; else xo_errx(1, "invalid AuthMethod at line %d; " "must be either \"none\" or \"CHAP\"", lineno); } ; header_digest: HEADER_DIGEST EQUALS STR { if (target->t_header_digest != DIGEST_UNSPECIFIED) xo_errx(1, "duplicated HeaderDigest at line %d", lineno); if (strcasecmp($3, "none") == 0) target->t_header_digest = DIGEST_NONE; else if (strcasecmp($3, "CRC32C") == 0) target->t_header_digest = DIGEST_CRC32C; else xo_errx(1, "invalid HeaderDigest at line %d; " "must be either \"none\" or \"CRC32C\"", lineno); } ; data_digest: DATA_DIGEST EQUALS STR { if (target->t_data_digest != DIGEST_UNSPECIFIED) xo_errx(1, "duplicated DataDigest at line %d", lineno); if (strcasecmp($3, "none") == 0) target->t_data_digest = DIGEST_NONE; else if (strcasecmp($3, "CRC32C") == 0) target->t_data_digest = DIGEST_CRC32C; else xo_errx(1, "invalid DataDigest at line %d; " "must be either \"none\" or \"CRC32C\"", lineno); } ; session_type: SESSION_TYPE EQUALS STR { if (target->t_session_type != SESSION_TYPE_UNSPECIFIED) xo_errx(1, "duplicated SessionType at line %d", lineno); if (strcasecmp($3, "normal") == 0) target->t_session_type = SESSION_TYPE_NORMAL; else if (strcasecmp($3, "discovery") == 0) target->t_session_type = SESSION_TYPE_DISCOVERY; else xo_errx(1, "invalid SessionType at line %d; " "must be either \"normal\" or \"discovery\"", lineno); } ; enable: ENABLE EQUALS STR { if (target->t_enable != ENABLE_UNSPECIFIED) xo_errx(1, "duplicated enable at line %d", lineno); target->t_enable = parse_enable($3); if (target->t_enable == ENABLE_UNSPECIFIED) xo_errx(1, "invalid enable at line %d; " "must be either \"on\" or \"off\"", lineno); } ; offload: OFFLOAD EQUALS STR { if (target->t_offload != NULL) xo_errx(1, "duplicated offload at line %d", lineno); target->t_offload = $3; } ; protocol: PROTOCOL EQUALS STR { if (target->t_protocol != PROTOCOL_UNSPECIFIED) xo_errx(1, "duplicated protocol at line %d", lineno); if (strcasecmp($3, "iscsi") == 0) target->t_protocol = PROTOCOL_ISCSI; else if (strcasecmp($3, "iser") == 0) target->t_protocol = PROTOCOL_ISER; else xo_errx(1, "invalid protocol at line %d; " "must be either \"iscsi\" or \"iser\"", lineno); } ; ignored: IGNORED EQUALS STR { xo_warnx("obsolete statement ignored at line %d", lineno); } ; dscp: DSCP EQUALS STR { uint64_t tmp; + if (target->t_dscp != -1) + xo_errx(1, "duplicated dscp at line %d", lineno); if (strcmp($3, "0x") == 0) { tmp = strtol($3 + 2, NULL, 16); } else if (expand_number($3, &tmp) != 0) { yyerror("invalid numeric value"); free($3); return(1); } if (tmp >= 0x40) { yyerror("invalid dscp value"); return(1); } target->t_dscp = tmp; } | DSCP EQUALS BE { target->t_dscp = IPTOS_DSCP_CS0 >> 2 ; } | DSCP EQUALS EF { target->t_dscp = IPTOS_DSCP_EF >> 2 ; } | DSCP EQUALS CS0 { target->t_dscp = IPTOS_DSCP_CS0 >> 2 ; } | DSCP EQUALS CS1 { target->t_dscp = IPTOS_DSCP_CS1 >> 2 ; } | DSCP EQUALS CS2 { target->t_dscp = IPTOS_DSCP_CS2 >> 2 ; } | DSCP EQUALS CS3 { target->t_dscp = IPTOS_DSCP_CS3 >> 2 ; } | DSCP EQUALS CS4 { target->t_dscp = IPTOS_DSCP_CS4 >> 2 ; } | DSCP EQUALS CS5 { target->t_dscp = IPTOS_DSCP_CS5 >> 2 ; } | DSCP EQUALS CS6 { target->t_dscp = IPTOS_DSCP_CS6 >> 2 ; } | DSCP EQUALS CS7 { target->t_dscp = IPTOS_DSCP_CS7 >> 2 ; } | DSCP EQUALS AF11 { target->t_dscp = IPTOS_DSCP_AF11 >> 2 ; } | DSCP EQUALS AF12 { target->t_dscp = IPTOS_DSCP_AF12 >> 2 ; } | DSCP EQUALS AF13 { target->t_dscp = IPTOS_DSCP_AF13 >> 2 ; } | DSCP EQUALS AF21 { target->t_dscp = IPTOS_DSCP_AF21 >> 2 ; } | DSCP EQUALS AF22 { target->t_dscp = IPTOS_DSCP_AF22 >> 2 ; } | DSCP EQUALS AF23 { target->t_dscp = IPTOS_DSCP_AF23 >> 2 ; } | DSCP EQUALS AF31 { target->t_dscp = IPTOS_DSCP_AF31 >> 2 ; } | DSCP EQUALS AF32 { target->t_dscp = IPTOS_DSCP_AF32 >> 2 ; } | DSCP EQUALS AF33 { target->t_dscp = IPTOS_DSCP_AF33 >> 2 ; } | DSCP EQUALS AF41 { target->t_dscp = IPTOS_DSCP_AF41 >> 2 ; } | DSCP EQUALS AF42 { target->t_dscp = IPTOS_DSCP_AF42 >> 2 ; } | DSCP EQUALS AF43 { target->t_dscp = IPTOS_DSCP_AF43 >> 2 ; } + ; + +pcp: PCP EQUALS STR + { + uint64_t tmp; + + if (target->t_pcp != -1) + xo_errx(1, "duplicated pcp at line %d", lineno); + + if (expand_number($3, &tmp) != 0) { + yyerror("invalid numeric value"); + free($3); + return(1); + } + if (!((tmp >=0) && (tmp <= 7))) { + yyerror("invalid pcp value"); + return(1); + } + + target->t_pcp = tmp; + } ; %% void yyerror(const char *str) { xo_errx(1, "error in configuration file at line %d near '%s': %s", lineno, yytext, str); } static void check_perms(const char *path) { struct stat sb; int error; error = stat(path, &sb); if (error != 0) { xo_warn("stat"); return; } if (sb.st_mode & S_IWOTH) { xo_warnx("%s is world-writable", path); } else if (sb.st_mode & S_IROTH) { xo_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. */ xo_warnx("%s is world-executable", path); } /* * XXX: Should we also check for owner != 0? */ } struct conf * conf_new_from_file(const char *path) { int error; conf = conf_new(); target = target_new(conf); yyin = fopen(path, "r"); if (yyin == NULL) xo_err(1, "unable to open configuration file %s", path); check_perms(path); lineno = 1; yyrestart(yyin); error = yyparse(); assert(error == 0); fclose(yyin); assert(target->t_nickname == NULL); target_delete(target); conf_verify(conf); return (conf); } Index: head/usr.bin/iscsictl/token.l =================================================================== --- head/usr.bin/iscsictl/token.l (revision 367022) +++ head/usr.bin/iscsictl/token.l (revision 367023) @@ -1,124 +1,125 @@ %{ /*- * 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 "iscsictl.h" #include "y.tab.h" int lineno; #define YY_DECL int yylex(void) extern int yylex(void); %} %option noinput %option nounput %option noyywrap %% HeaderDigest { return HEADER_DIGEST; } DataDigest { return DATA_DIGEST; } TargetName { return TARGET_NAME; } TargetAddress { return TARGET_ADDRESS; } InitiatorName { return INITIATOR_NAME; } InitiatorAddress { return INITIATOR_ADDRESS; } InitiatorAlias { return INITIATOR_ALIAS; } chapIName { return USER; } chapSecret { return SECRET; } tgtChapName { return MUTUAL_USER; } tgtChapSecret { return MUTUAL_SECRET; } AuthMethod { return AUTH_METHOD; } SessionType { return SESSION_TYPE; } enable { return ENABLE; } protocol { return PROTOCOL; } offload { return OFFLOAD; } port { return IGNORED; } dscp { return DSCP; } +pcp { return PCP; } MaxConnections { return IGNORED; } TargetAlias { return IGNORED; } TargetPortalGroupTag { return IGNORED; } InitialR2T { return IGNORED; } ImmediateData { return IGNORED; } MaxRecvDataSegmentLength { return IGNORED; } MaxBurstLength { return IGNORED; } FirstBurstLength { return IGNORED; } DefaultTime2Wait { return IGNORED; } DefaultTime2Retain { return IGNORED; } MaxOutstandingR2T { return IGNORED; } DataPDUInOrder { return IGNORED; } DataSequenceInOrder { return IGNORED; } ErrorRecoveryLevel { return IGNORED; } tags { return IGNORED; } maxluns { return IGNORED; } sockbufsize { return IGNORED; } chapDigest { return IGNORED; } 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; } = { return EQUALS; } ; { return SEMICOLON; } #.*$ /* ignore comments */; \r\n { lineno++; } \n { lineno++; } [ \t]+ /* ignore whitespace */; . { yylval.str = strdup(yytext); return STR; } %% Index: head/usr.sbin/iscsid/iscsid.c =================================================================== --- head/usr.sbin/iscsid/iscsid.c (revision 367022) +++ head/usr.sbin/iscsid/iscsid.c (revision 367023) @@ -1,662 +1,681 @@ /*- * 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 #include #include #include #include "iscsid.h" static volatile bool sigalrm_received = false; static int nchildren = 0; static void usage(void) { fprintf(stderr, "usage: iscsid [-P pidfile][-d][-m maxproc][-t timeout]\n"); exit(1); } char * checked_strdup(const char *s) { char *c; c = strdup(s); if (c == NULL) log_err(1, "strdup"); return (c); } static void resolve_addr(const struct connection *conn, const char *address, struct addrinfo **ai, bool initiator_side) { struct addrinfo hints; char *arg, *addr, *ch; const char *port; int error, colons = 0; arg = checked_strdup(address); if (arg[0] == '\0') { fail(conn, "empty address"); log_errx(1, "empty address"); } if (arg[0] == '[') { /* * IPv6 address in square brackets, perhaps with port. */ arg++; addr = strsep(&arg, "]"); if (arg == NULL) { fail(conn, "malformed address"); log_errx(1, "malformed address %s", address); } if (arg[0] == '\0') { port = NULL; } else if (arg[0] == ':') { port = arg + 1; } else { fail(conn, "malformed address"); log_errx(1, "malformed address %s", address); } } 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 = NULL; } else { addr = strsep(&arg, ":"); if (arg == NULL) port = NULL; else port = arg; } } if (port == NULL && !initiator_side) port = "3260"; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; if (initiator_side) hints.ai_flags |= AI_PASSIVE; error = getaddrinfo(addr, port, &hints, ai); if (error != 0) { fail(conn, gai_strerror(error)); log_errx(1, "getaddrinfo for %s failed: %s", address, gai_strerror(error)); } } static struct connection * connection_new(int iscsi_fd, const struct iscsi_daemon_request *request) { struct connection *conn; struct iscsi_session_limits *isl; struct addrinfo *from_ai, *to_ai; const char *from_addr, *to_addr; #ifdef ICL_KERNEL_PROXY struct iscsi_daemon_connect idc; #endif int error, sockbuf; conn = calloc(1, sizeof(*conn)); if (conn == NULL) log_err(1, "calloc"); /* * Default values, from RFC 3720, section 12. */ conn->conn_protocol_level = 0; conn->conn_header_digest = CONN_DIGEST_NONE; conn->conn_data_digest = CONN_DIGEST_NONE; conn->conn_initial_r2t = true; conn->conn_immediate_data = true; 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_iscsi_fd = iscsi_fd; conn->conn_session_id = request->idr_session_id; memcpy(&conn->conn_conf, &request->idr_conf, sizeof(conn->conn_conf)); memcpy(&conn->conn_isid, &request->idr_isid, sizeof(conn->conn_isid)); conn->conn_tsih = request->idr_tsih; /* * Read the driver limits and provide reasonable defaults for the ones * the driver doesn't care about. If a max_snd_dsl is not explicitly * provided by the driver then we'll make sure both conn->max_snd_dsl * and isl->max_snd_dsl are set to the rcv_dsl. This preserves historic * behavior. */ isl = &conn->conn_limits; memcpy(isl, &request->idr_limits, sizeof(*isl)); if (isl->isl_max_recv_data_segment_length == 0) isl->isl_max_recv_data_segment_length = (1 << 24) - 1; if (isl->isl_max_send_data_segment_length == 0) isl->isl_max_send_data_segment_length = isl->isl_max_recv_data_segment_length; if (isl->isl_max_burst_length == 0) isl->isl_max_burst_length = (1 << 24) - 1; if (isl->isl_first_burst_length == 0) isl->isl_first_burst_length = (1 << 24) - 1; if (isl->isl_first_burst_length > isl->isl_max_burst_length) isl->isl_first_burst_length = isl->isl_max_burst_length; /* * Limit default send length in case it won't be negotiated. * We can't do it for other limits, since they may affect both * sender and receiver operation, and we must obey defaults. */ if (conn->conn_max_send_data_segment_length > isl->isl_max_send_data_segment_length) { conn->conn_max_send_data_segment_length = isl->isl_max_send_data_segment_length; } from_addr = conn->conn_conf.isc_initiator_addr; to_addr = conn->conn_conf.isc_target_addr; if (from_addr[0] != '\0') resolve_addr(conn, from_addr, &from_ai, true); else from_ai = NULL; resolve_addr(conn, to_addr, &to_ai, false); #ifdef ICL_KERNEL_PROXY if (conn->conn_conf.isc_iser) { memset(&idc, 0, sizeof(idc)); idc.idc_session_id = conn->conn_session_id; if (conn->conn_conf.isc_iser) idc.idc_iser = 1; idc.idc_domain = to_ai->ai_family; idc.idc_socktype = to_ai->ai_socktype; idc.idc_protocol = to_ai->ai_protocol; if (from_ai != NULL) { idc.idc_from_addr = from_ai->ai_addr; idc.idc_from_addrlen = from_ai->ai_addrlen; } idc.idc_to_addr = to_ai->ai_addr; idc.idc_to_addrlen = to_ai->ai_addrlen; log_debugx("connecting to %s using ICL kernel proxy", to_addr); error = ioctl(iscsi_fd, ISCSIDCONNECT, &idc); if (error != 0) { fail(conn, strerror(errno)); log_err(1, "failed to connect to %s " "using ICL kernel proxy: ISCSIDCONNECT", to_addr); } return (conn); } #endif /* ICL_KERNEL_PROXY */ if (conn->conn_conf.isc_iser) { fail(conn, "iSER not supported"); log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY " "does not support iSER"); } conn->conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype, to_ai->ai_protocol); if (conn->conn_socket < 0) { fail(conn, strerror(errno)); log_err(1, "failed to create socket for %s", from_addr); } sockbuf = SOCKBUF_SIZE; if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_RCVBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_RCVBUF) failed"); sockbuf = SOCKBUF_SIZE; if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_SNDBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_SNDBUF) failed"); if (conn->conn_conf.isc_dscp != -1) { int tos = conn->conn_conf.isc_dscp << 2; if (to_ai->ai_family == AF_INET) { if (setsockopt(conn->conn_socket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) log_warn("setsockopt(IP_TOS) " "failed for %s", from_addr); } else if (to_ai->ai_family == AF_INET6) { if (setsockopt(conn->conn_socket, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) == -1) log_warn("setsockopt(IPV6_TCLASS) " "failed for %s", from_addr); } } + if (conn->conn_conf.isc_pcp != -1) { + int pcp = conn->conn_conf.isc_pcp; + if (to_ai->ai_family == AF_INET) { + if (setsockopt(conn->conn_socket, + IPPROTO_IP, IP_VLAN_PCP, + &pcp, sizeof(pcp)) == -1) + log_warn("setsockopt(IP_VLAN_PCP) " + "failed for %s", + from_addr); + } else + if (to_ai->ai_family == AF_INET6) { + if (setsockopt(conn->conn_socket, + IPPROTO_IPV6, IPV6_VLAN_PCP, + &pcp, sizeof(pcp)) == -1) + log_warn("setsockopt(IPV6_VLAN_PCP) " + "failed for %s", + from_addr); + } + } if (from_ai != NULL) { error = bind(conn->conn_socket, from_ai->ai_addr, from_ai->ai_addrlen); if (error != 0) { fail(conn, strerror(errno)); log_err(1, "failed to bind to %s", from_addr); } } log_debugx("connecting to %s", to_addr); error = connect(conn->conn_socket, to_ai->ai_addr, to_ai->ai_addrlen); if (error != 0) { fail(conn, strerror(errno)); log_err(1, "failed to connect to %s", to_addr); } return (conn); } static void handoff(struct connection *conn) { struct iscsi_daemon_handoff idh; int error; log_debugx("handing off connection to the kernel"); memset(&idh, 0, sizeof(idh)); idh.idh_session_id = conn->conn_session_id; idh.idh_socket = conn->conn_socket; strlcpy(idh.idh_target_alias, conn->conn_target_alias, sizeof(idh.idh_target_alias)); idh.idh_tsih = conn->conn_tsih; idh.idh_statsn = conn->conn_statsn; idh.idh_protocol_level = conn->conn_protocol_level; idh.idh_header_digest = conn->conn_header_digest; idh.idh_data_digest = conn->conn_data_digest; idh.idh_initial_r2t = conn->conn_initial_r2t; idh.idh_immediate_data = conn->conn_immediate_data; idh.idh_max_recv_data_segment_length = conn->conn_max_recv_data_segment_length; idh.idh_max_send_data_segment_length = conn->conn_max_send_data_segment_length; idh.idh_max_burst_length = conn->conn_max_burst_length; idh.idh_first_burst_length = conn->conn_first_burst_length; error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh); if (error != 0) log_err(1, "ISCSIDHANDOFF"); } void fail(const struct connection *conn, const char *reason) { struct iscsi_daemon_fail idf; int error, saved_errno; saved_errno = errno; memset(&idf, 0, sizeof(idf)); idf.idf_session_id = conn->conn_session_id; strlcpy(idf.idf_reason, reason, sizeof(idf.idf_reason)); error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, &idf); if (error != 0) log_err(1, "ISCSIDFAIL"); errno = saved_errno; } /* * XXX: I CANT INTO LATIN */ static void capsicate(struct connection *conn) { cap_rights_t rights; #ifdef ICL_KERNEL_PROXY const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE, ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE, ISCSISMODIFY }; #else const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE, ISCSISMODIFY }; #endif cap_rights_init(&rights, CAP_IOCTL); if (caph_rights_limit(conn->conn_iscsi_fd, &rights) < 0) log_err(1, "cap_rights_limit"); if (caph_ioctls_limit(conn->conn_iscsi_fd, cmds, nitems(cmds)) < 0) log_err(1, "cap_ioctls_limit"); if (caph_enter() != 0) log_err(1, "cap_enter"); if (cap_sandboxed()) log_debugx("Capsicum capability mode enabled"); else log_warnx("Capsicum capability mode not supported"); } bool timed_out(void) { return (sigalrm_received); } static void sigalrm_handler(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 set_timeout(int timeout) { struct sigaction sa; struct itimerval itv; int error; if (timeout <= 0) { log_debugx("session timeout disabled"); return; } bzero(&sa, sizeof(sa)); 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. */ bzero(&itv, sizeof(itv)); itv.it_interval.tv_sec = 1; itv.it_value.tv_sec = timeout; log_debugx("setting session timeout to %d seconds", timeout); error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); } 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_sigchld(void) { struct sigaction sa; int error; bzero(&sa, sizeof(sa)); sa.sa_handler = sigchld_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } static void handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int timeout) { struct connection *conn; log_set_peer_addr(request->idr_conf.isc_target_addr); if (request->idr_conf.isc_target[0] != '\0') { log_set_peer_name(request->idr_conf.isc_target); setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target); } else { setproctitle("%s", request->idr_conf.isc_target_addr); } conn = connection_new(iscsi_fd, request); set_timeout(timeout); capsicate(conn); login(conn); if (conn->conn_conf.isc_discovery != 0) discovery(conn); else handoff(conn); log_debugx("nothing more to do; exiting"); exit (0); } 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); } int main(int argc, char **argv) { int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno, timeout = 60; bool dont_daemonize = false; struct pidfh *pidfh; pid_t pid, otherpid; const char *pidfile_path = DEFAULT_PIDFILE; struct iscsi_daemon_request request; while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) { switch (ch) { case 'P': pidfile_path = optarg; break; case 'd': dont_daemonize = true; debug++; break; case 'l': debug = atoi(optarg); break; case 'm': maxproc = atoi(optarg); break; case 't': timeout = atoi(optarg); break; case '?': default: usage(); } } argc -= optind; if (argc != 0) usage(); log_init(debug); pidfh = pidfile_open(pidfile_path, 0600, &otherpid); if (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\"", pidfile_path); } iscsi_fd = open(ISCSI_PATH, O_RDWR); if (iscsi_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("iscsi"); if (retval != -1) iscsi_fd = open(ISCSI_PATH, O_RDWR); else errno = saved_errno; } if (iscsi_fd < 0) log_err(1, "failed to open %s", ISCSI_PATH); if (dont_daemonize == false) { if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(pidfh); exit(1); } } pidfile_write(pidfh); register_sigchld(); for (;;) { log_debugx("waiting for request from the kernel"); memset(&request, 0, sizeof(request)); error = ioctl(iscsi_fd, ISCSIDWAIT, &request); if (error != 0) { if (errno == EINTR) { nchildren -= wait_for_children(false); assert(nchildren >= 0); continue; } log_err(1, "ISCSIDWAIT"); } if (dont_daemonize) { log_debugx("not forking due to -d flag; " "will exit after servicing a single request"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (maxproc > 0 && nchildren >= maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", 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) continue; } pidfile_close(pidfh); handle_request(iscsi_fd, &request, timeout); } return (0); }