The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=66b5296f1b29083634e2875ff08c32e7b6b866a8
commit 66b5296f1b29083634e2875ff08c32e7b6b866a8 Author: John Baldwin <j...@freebsd.org> AuthorDate: 2025-08-06 19:57:50 +0000 Commit: John Baldwin <j...@freebsd.org> CommitDate: 2025-08-06 19:59:13 +0000 ctld: Add support for NVMe over Fabrics While the overall structure is similar for NVMeoF controllers and iSCSI targets, there are sufficient differences that NVMe support uses an alternate configuration syntax. - In authentication groups, permitted NVMeoF hosts can be allowed by names (NQNs) via "host-nqn" values (similar to "initiator-name" for iSCSI). Similarly, "host-address" accepts permitted host addresses similar to "initiator-portal" for iSCSI. - A new "transport-group" context enumerates transports that can be used by a group of NVMeoF controllers similar to the "portal-group" context for iSCSI. In this section, the "listen" keyword accepts a transport as well as an address to permit other types of transports besides TCP in the future. The "foreign", "offload", and "redirect" keywords are also not meaningful and thus not supported. - A new "controller" context describes an NVMeoF I/O controller similar to the "target" context for iSCSI. One key difference here is that "lun" objects are replaced by "namespace" objects. However, a "namespace" can reference a named global lun permitting LUNs to be shared between iSCSI targets and NVMeoF controllers. NB: Authentication via CHAP is not implemented for NVMeoF. Reviewed by: imp Sponsored by: Chelsio Communications Differential Revision: https://reviews.freebsd.org/D48773 --- usr.sbin/ctld/Makefile | 6 +- usr.sbin/ctld/conf.cc | 67 ++++++ usr.sbin/ctld/conf.h | 12 + usr.sbin/ctld/ctl.conf.5 | 242 ++++++++++++++++++- usr.sbin/ctld/ctld.cc | 166 +++++++++++++ usr.sbin/ctld/ctld.hh | 43 +++- usr.sbin/ctld/discovery.cc | 3 + usr.sbin/ctld/kernel.cc | 54 ++++- usr.sbin/ctld/nvmf.cc | 478 ++++++++++++++++++++++++++++++++++++ usr.sbin/ctld/nvmf.hh | 71 ++++++ usr.sbin/ctld/nvmf_discovery.cc | 518 ++++++++++++++++++++++++++++++++++++++++ usr.sbin/ctld/parse.y | 232 +++++++++++++++++- usr.sbin/ctld/token.l | 7 + usr.sbin/ctld/uclparse.cc | 381 +++++++++++++++++++++++++++++ 14 files changed, 2263 insertions(+), 17 deletions(-) diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile index ad3a2661794a..61efe8a05cfb 100644 --- a/usr.sbin/ctld/Makefile +++ b/usr.sbin/ctld/Makefile @@ -6,19 +6,21 @@ CFLAGS+=-I${SRCTOP}/contrib/libucl/include PACKAGE= ctl PROG_CXX= ctld SRCS= ctld.cc conf.cc discovery.cc iscsi.cc isns.cc kernel.cc -SRCS+= login.cc parse.y token.l y.tab.h uclparse.cc +SRCS+= login.cc nvmf.cc nvmf_discovery.cc +SRCS+= parse.y token.l y.tab.h uclparse.cc CFLAGS+= -I${.CURDIR} CFLAGS+= -I${SRCTOP}/sys CFLAGS+= -I${SRCTOP}/sys/cam/ctl CFLAGS+= -I${SRCTOP}/sys/dev/iscsi CFLAGS+= -I${SRCTOP}/lib/libiscsiutil CFLAGS+= -I${SRCTOP}/lib/libutil++ +CFLAGS+= -I${SRCTOP}/lib/libnvmf #CFLAGS+= -DICL_KERNEL_PROXY NO_WCAST_ALIGN= CXXWARNFLAGS.gcc= -Wno-shadow MAN= ctld.8 ctl.conf.5 -LIBADD= bsdxml iscsiutil md sbuf util ucl m nv util++ +LIBADD= bsdxml iscsiutil nvmf md sbuf util ucl m nv util++ YFLAGS+= -v CLEANFILES= y.tab.c y.tab.h y.output diff --git a/usr.sbin/ctld/conf.cc b/usr.sbin/ctld/conf.cc index d8f941e0bc52..ab76f8e2ed0b 100644 --- a/usr.sbin/ctld/conf.cc +++ b/usr.sbin/ctld/conf.cc @@ -122,6 +122,18 @@ auth_group_add_chap_mutual(const char *user, const char *secret, return (auth_group->add_chap_mutual(user, secret, user2, secret2)); } +bool +auth_group_add_host_address(const char *portal) +{ + return (auth_group->add_host_address(portal)); +} + +bool +auth_group_add_host_nqn(const char *name) +{ + return (auth_group->add_host_nqn(name)); +} + bool auth_group_add_initiator_name(const char *name) { @@ -233,6 +245,29 @@ portal_group_set_tag(uint16_t tag) portal_group->set_tag(tag); } +bool +transport_group_start(const char *name) +{ + if (strcmp(name, "default") == 0) + portal_group = conf->define_default_transport_group(); + else + portal_group = conf->add_transport_group(name); + return (portal_group != NULL); +} + +bool +transport_group_add_listen_discovery_tcp(const char *listen) +{ + return portal_group->add_portal(listen, + portal_protocol::NVME_DISCOVERY_TCP); +} + +bool +transport_group_add_listen_tcp(const char *listen) +{ + return portal_group->add_portal(listen, portal_protocol::NVME_TCP); +} + bool lun_start(const char *name) { @@ -387,6 +422,38 @@ target_start_lun(u_int id) return (lun != nullptr); } +bool +controller_start(const char *name) +{ + target = conf->add_controller(name); + return (target != nullptr); +} + +bool +controller_add_host_address(const char *addr) +{ + return (target->add_host_address(addr)); +} + +bool +controller_add_host_nqn(const char *name) +{ + return (target->add_host_nqn(name)); +} + +bool +controller_add_namespace(u_int id, const char *name) +{ + return (target->add_namespace(id, name)); +} + +bool +controller_start_namespace(u_int id) +{ + lun = target->start_namespace(id); + return (lun != nullptr); +} + bool parse_conf(const char *path) { diff --git a/usr.sbin/ctld/conf.h b/usr.sbin/ctld/conf.h index b13fd80e9fe5..642c8f234d30 100644 --- a/usr.sbin/ctld/conf.h +++ b/usr.sbin/ctld/conf.h @@ -43,6 +43,8 @@ void auth_group_finish(void); bool auth_group_add_chap(const char *user, const char *secret); bool auth_group_add_chap_mutual(const char *user, const char *secret, const char *user2, const char *secret2); +bool auth_group_add_host_address(const char *portal); +bool auth_group_add_host_nqn(const char *name); bool auth_group_add_initiator_name(const char *name); bool auth_group_add_initiator_portal(const char *portal); bool auth_group_set_type(const char *type); @@ -69,6 +71,10 @@ bool portal_group_set_pcp(u_int pcp); bool portal_group_set_redirection(const char *addr); void portal_group_set_tag(uint16_t tag); +bool transport_group_start(const char *name); +bool transport_group_add_listen_discovery_tcp(const char *listen); +bool transport_group_add_listen_tcp(const char *listen); + bool target_start(const char *name); void target_finish(void); bool target_add_chap(const char *user, const char *secret); @@ -85,6 +91,12 @@ bool target_set_physical_port(const char *pport); bool target_set_redirection(const char *addr); bool target_start_lun(u_int id); +bool controller_start(const char *name); +bool controller_add_host_address(const char *addr); +bool controller_add_host_nqn(const char *name); +bool controller_add_namespace(u_int id, const char *name); +bool controller_start_namespace(u_int id); + bool lun_start(const char *name); void lun_finish(void); bool lun_add_option(const char *name, const char *value); diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5 index e42dd8067006..12f4186a6844 100644 --- a/usr.sbin/ctld/ctl.conf.5 +++ b/usr.sbin/ctld/ctl.conf.5 @@ -26,12 +26,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 26, 2025 +.Dd August 6, 2025 .Dt CTL.CONF 5 .Os .Sh NAME .Nm ctl.conf -.Nd CAM Target Layer / iSCSI target daemon configuration file +.Nd CAM Target Layer / iSCSI target / NVMeoF controller daemon configuration file .Sh DESCRIPTION The .Nm @@ -59,6 +59,11 @@ file is: .Dl ... } +.No transport-group Ar name No { +.Dl listen Ar transport Ar address +.Dl ... +} + .No target Ar name { .Dl auth-group Ar name .Dl portal-group Ar name @@ -67,6 +72,15 @@ file is: .Dl } .Dl ... } + +.No controller Ar name { +.Dl auth-group Ar name +.Dl transport-group Ar name +.Dl namespace Ar number No { +.Dl path Ar path +.Dl } +.Dl ... +} .Ed .Ss Global Context .Bl -tag -width indent @@ -94,16 +108,29 @@ Create a configuration context, defining a new portal-group, which can then be assigned to any number of targets. +.It Ic transport-group Ar name +Create a +.Sy transport-group +configuration context, +defining a new transport-group, +which can then be assigned to any number of NVMeoF controllers. .It Ic lun Ar name Create a .Sy lun -configuration context, defining a LUN to be exported by any number of targets. +configuration context, defining a LUN to be exported by any number of targets +or controllers. .It Ic target Ar name Create a .Sy target configuration context, which can optionally contain one or more .Sy lun contexts. +.It Ic controller Ar name +Create a +.Sy controller +configuration context, which can optionally contain one or more +.Sy namespace +contexts. .It Ic timeout Ar seconds The timeout for login sessions, after which the connection will be forcibly terminated. @@ -150,6 +177,19 @@ the configuration may only contain either or .Sy chap-mutual entries; it is an error to mix them. +.It Ic host-address Ar address Ns Op / Ns Ar prefixlen +An NVMeoF host address: an IPv4 or IPv6 address, optionally +followed by a literal slash and a prefix length. +Only NVMeoF hosts with an address matching one of the defined +addresses will be allowed to connect. +If not defined, there will be no restrictions based on host +address. +.It Ic host-nqn Ar name +An NVMeoF host name. +Only NVMeoF hosts with a name matching one of the defined +names will be allowed to connect. +If not defined, there will be no restrictions based on NVMe host +name. .It Ic initiator-name Ar initiator-name An iSCSI initiator name. Only initiators with a name matching one of the defined @@ -264,6 +304,75 @@ to .Qq Ar 7 . When omitted, the default for the outgoing interface is used. .El +.Ss transport-group Context +.Bl -tag -width indent +.It Ic discovery-auth-group Ar name +See the description for this option for +.Sy portal-group +contexts. +.It Ic discovery-filter Ar filter +Filter can be either +.Qq Ar none , +.Qq Ar address , +or +.Qq Ar address-name . +When set to +.Qq Ar none , +discovery will return all controllers assigned to that transport group. +When set to +.Qq Ar address , +discovery will not return controllers that cannot be accessed by the +host because of their +.Sy host-address . +When set to +.Qq Ar address-name , +the check will include both +.Sy host-address +and +.Sy host-nqn . +The default is +.Qq Ar none . +.It Ic listen Ar transport Ar address +An IPv4 or IPv6 address and port to listen on for incoming connections +using the specified NVMeoF transport. +Supported transports are +.Qq Ar tcp +.Pq for NVMe/TCP I/O controllers +and +.Qq Ar discovery-tcp +.Pq for NVMe/TCP discovery controllers . +.It Ic option Ar name Ar value +One of the following options: +.Bl -column "max_admin_qsize" "Default" "Transports" +.It Sy Name Ta Sy Default Ta Sy Transports Ta Sy Description +.It MAXH2CDATA Ta 256KiB Ta TCP Ta +Size in bytes of the maximum data payload size for data PDUs accepted from +remote hosts. +The value must be at least 4KiB and must be a multiple of 4. +.It SQFC Ta false Ta any Ta +Always enable SQ flow control. +.It HDGST Ta false Ta TCP Ta +Enable PDU header digests if requested by a remote host. +.It DDGST Ta false Ta TCP Ta +Enable PDU data digests if requested by a remote host. +.It max_admin_qsize Ta 4096 Ta any Ta +The maximum number of entries a remote host can request for an admin queue pair. +.It max_io_qsize Ta 65536 Ta any Ta +The maximum number of entries a remote host can request for an I/O queue pair. +.El +.It Ic tag Ar value +Unique 16-bit port ID for this +.Sy transport-group . +If not specified, the value is generated automatically. +.It Ic dscp Ar value +See the description for this option for +.Sy portal-group +contexts. +.It Ic pcp Ar value +See the description for this option for +.Sy portal-group +contexts. +.El .Ss target Context .Bl -tag -width indent .It Ic alias Ar text @@ -390,6 +499,101 @@ configuration context, defining a LUN exported by the parent target. 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 controller Context +.Bl -tag -width indent +.It Ic auth-group Ar name +Assign a previously defined authentication group to the controller. +By default, controllers that do not specify their own auth settings, +using clauses such as +.Sy host-address +or +.Sy host-nqn , +are assigned to the +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 transport-group +clause. +.It Ic auth-type Ar type +Sets the authentication type. +Type can be either +.Qq Ar none +or +.Qq Ar deny . +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 controller . +This clause is mutually exclusive with +.Sy auth-group ; +one cannot use +both in a single controller. +.It Ic host-address Ar address Ns Op / Ns Ar prefixlen +An NVMeoF host address: an IPv4 or IPv6 address, optionally +followed by a literal slash and a prefix length. +Only NVMeoF hosts with an address matching one of the defined +addresses will be allowed to connect. +If not defined, there will be no restrictions based on host +address. +This clause is mutually exclusive with +.Sy auth-group ; +one cannot use +both in a single controller. +.It Ic host-nqn Ar name +An NVMeoF host name. +Only NVMeoF hosts with a name matching one of the defined +names will be allowed to connect. +If not defined, there will be no restrictions based on NVMe host +name. +This clause is mutually exclusive with +.Sy auth-group ; +one cannot use +both in a single target. +.Pp +The +.Sy auth-type , +.Sy host-address , +and +.Sy host-nqn +clauses in the controller context provide an alternative to assigning an +.Sy auth-group +defined separately, useful in the common case of authentication settings +specific to a single controller. +.It Ic transport-group Ar name Op Ar ag-name +Assign a previously defined transport group to the controller. +The default transport group is +.Qq Ar default , +which makes the controller available +on TCP port 4420 on all configured IPv4 and IPv6 addresses. +The optional second argument specifies the +.Sy auth-group +for connections to this specific transport group group. +If the second argument is not specified, the controller +.Sy auth-group +is used. +.It Ic namespace Ar number Ar name +Export previously defined +.Sy lun +as an NVMe namespace from the parent controller. +.It Ic namespace Ar number +Create a +.Sy namespace +configuration context, defining an NVMe namespace exported by the parent target. +.Pp +This is an alternative to defining the namespace separately, +useful in the common case of a namespace being exported by a single controller. +.Sy namespace +configuration contexts accept the the same properties as +.Sy lun +contexts. +.El .Ss lun Context .Bl -tag -width indent .It Ic backend Ar block No | Ar ramdisk @@ -410,7 +614,7 @@ 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. +The SCSI Device Identification string presented to iSCSI initiators. .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) @@ -425,11 +629,11 @@ section of 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 +For optimal performance, create ZFS volumes with the .Qq Ar volmode=dev property set. .It Ic serial Ar string -The SCSI serial number presented to the initiator. +The SCSI serial number presented to iSCSI initiators. .It Ic size Ar size The LUN size, in bytes or by number with a suffix of .Sy K , M , G , T @@ -498,6 +702,16 @@ target naa.50015178f369f092 { port isp1 lun 0 example_1 } + +controller nqn.2012-06.com.example:controller1 { + auth-group no-authentication; + namespace 1 example_1 + namespace 2 { + backend ramdisk + size 1G + option capacity 1G + } +} .Ed .Pp An equivalent configuration in UCL format, for use with @@ -585,6 +799,22 @@ target { } } } + +controller { + "nqn.2012-06.com.example:controller1" { + auth-group = no-authentication + namespace = { + 1 = example_1, + 2 { + backend = ramdisk + size = 1G + options { + capacity = 1G + } + } + } + } +} .Ed .Sh SEE ALSO .Xr ctl 4 , diff --git a/usr.sbin/ctld/ctld.cc b/usr.sbin/ctld/ctld.cc index 451245b8d5fa..10c12f25068e 100644 --- a/usr.sbin/ctld/ctld.cc +++ b/usr.sbin/ctld/ctld.cc @@ -41,6 +41,7 @@ #include <assert.h> #include <ctype.h> #include <errno.h> +#include <libnvmf.h> #include <netdb.h> #include <signal.h> #include <stdbool.h> @@ -67,6 +68,8 @@ static volatile bool sigalrm_received = false; static int kqfd; static int nchildren = 0; +uint32_t conf::global_genctr; + static void usage(void) { @@ -76,6 +79,11 @@ usage(void) exit(1); } +conf::conf() +{ + conf_genctr = global_genctr++; +} + void conf::set_debug(int debug) { @@ -277,6 +285,23 @@ auth_group::add_chap_mutual(const char *user, const char *secret, return (true); } +bool +auth_group::add_host_nqn(std::string_view nqn) +{ + /* Silently ignore duplicates. */ + ag_host_names.emplace(nqn); + return (true); +} + +bool +auth_group::host_permitted(std::string_view nqn) const +{ + if (ag_host_names.empty()) + return (true); + + return (ag_host_names.count(std::string(nqn)) != 0); +} + bool auth_group::add_initiator_name(std::string_view name) { @@ -360,6 +385,20 @@ auth_portal::parse(const char *portal) return true; } +bool +auth_group::add_host_address(const char *address) +{ + auth_portal ap; + if (!ap.parse(address)) { + log_warnx("invalid controller address \"%s\" for %s", address, + label()); + return (false); + } + + ag_host_addresses.emplace_back(ap); + return (true); +} + bool auth_group::add_initiator_portal(const char *portal) { @@ -406,6 +445,18 @@ auth_portal::matches(const struct sockaddr *sa) const return (true); } +bool +auth_group::host_permitted(const struct sockaddr *sa) const +{ + if (ag_host_addresses.empty()) + return (true); + + for (const auth_portal &ap : ag_host_addresses) + if (ap.matches(sa)) + return (true); + return (false); +} + bool auth_group::initiator_permitted(const struct sockaddr *sa) const { @@ -501,6 +552,45 @@ conf::find_portal_group(std::string_view name) return (it->second.get()); } +struct portal_group * +conf::add_transport_group(const char *name) +{ + auto pair = conf_transport_groups.try_emplace(name, + nvmf_make_transport_group(this, name)); + if (!pair.second) { + log_warnx("duplicated transport-group \"%s\"", name); + return (nullptr); + } + + return (pair.first->second.get()); +} + +/* + * Make it possible to redefine the default transport-group, but only + * once. + */ +struct portal_group * +conf::define_default_transport_group() +{ + if (conf_default_tg_defined) { + log_warnx("duplicated transport-group \"default\""); + return (nullptr); + } + + conf_default_tg_defined = true; + return (find_transport_group("default")); +} + +struct portal_group * +conf::find_transport_group(std::string_view name) +{ + auto it = conf_transport_groups.find(std::string(name)); + if (it == conf_transport_groups.end()) + return (nullptr); + + return (it->second.get()); +} + bool portal_group::is_dummy() const { @@ -1113,6 +1203,40 @@ portal_group::find_port(std::string_view target) const return (it->second); } +struct target * +conf::add_controller(const char *name) +{ + if (!nvmf_nqn_valid_strict(name)) { + log_warnx("controller name \"%s\" is invalid for NVMe", name); + return nullptr; + } + + /* + * Normalize the name to lowercase to match iSCSI. + */ + std::string t_name(name); + for (char &c : t_name) + c = tolower(c); + + auto const &pair = conf_controllers.try_emplace(t_name, + nvmf_make_controller(this, t_name)); + if (!pair.second) { + log_warnx("duplicated controller \"%s\"", name); + return nullptr; + } + + return pair.first->second.get(); +} + +struct target * +conf::find_controller(std::string_view name) +{ + auto it = conf_controllers.find(std::string(name)); + if (it == conf_controllers.end()) + return nullptr; + return it->second.get(); +} + target::target(struct conf *conf, const char *keyword, std::string_view name) : t_conf(conf), t_name(name) { @@ -1367,6 +1491,8 @@ conf::delete_target_luns(struct lun *lun) { for (const auto &kv : conf_targets) kv.second->remove_lun(lun); + for (const auto &kv : conf_controllers) + kv.second->remove_lun(lun); } struct lun * @@ -1667,9 +1793,15 @@ conf::verify() for (auto &kv : conf_targets) { kv.second->verify(); } + for (auto &kv : conf_controllers) { + kv.second->verify(); + } for (auto &kv : conf_portal_groups) { kv.second->verify(this); } + for (auto &kv : conf_transport_groups) { + kv.second->verify(this); + } for (const auto &kv : conf_auth_groups) { const std::string &ag_name = kv.first; if (ag_name == "default" || @@ -1813,6 +1945,12 @@ conf::reuse_portal_group_socket(struct portal &newp) if (pg.reuse_socket(newp)) return (true); } + for (auto &kv : conf_transport_groups) { + struct portal_group &pg = *kv.second; + + if (pg.reuse_socket(newp)) + return (true); + } return (false); } @@ -1864,6 +2002,17 @@ conf::apply(struct conf *oldconf) else newpg.allocate_tag(); } + for (auto &kv : conf_transport_groups) { + struct portal_group &newpg = *kv.second; + + if (newpg.tag() != 0) + continue; + auto it = oldconf->conf_transport_groups.find(kv.first); + if (it != oldconf->conf_transport_groups.end()) + newpg.set_tag(it->second->tag()); + else + newpg.allocate_tag(); + } /* Deregister on removed iSNS servers. */ for (auto &kv : oldconf->conf_isns) { @@ -2027,6 +2176,9 @@ conf::apply(struct conf *oldconf) for (auto &kv : conf_portal_groups) { cumulated_error += kv.second->open_sockets(*oldconf); } + for (auto &kv : conf_transport_groups) { + cumulated_error += kv.second->open_sockets(*oldconf); + } /* * Go through the no longer used sockets, closing them. @@ -2034,6 +2186,9 @@ conf::apply(struct conf *oldconf) for (auto &kv : oldconf->conf_portal_groups) { kv.second->close_sockets(); } + for (auto &kv : oldconf->conf_transport_groups) { + kv.second->close_sockets(); + } /* (Re-)Register on remaining/new iSNS servers. */ for (auto &kv : conf_isns) { @@ -2397,6 +2552,9 @@ conf_new_from_file(const char *path, bool ucl) pg = conf->add_portal_group("default"); assert(pg != NULL); + pg = conf->add_transport_group("default"); + assert(pg != NULL); + conf_start(conf.get()); if (ucl) valid = uclparse_conf(path); @@ -2427,6 +2585,14 @@ conf_new_from_file(const char *path, bool ucl) pg->add_default_portals(); } + if (!conf->default_portal_group_defined()) { + log_debugx("transport-group \"default\" not defined; " + "going with defaults"); + pg = conf->find_transport_group("default"); + assert(pg != NULL); + pg->add_default_portals(); + } + if (!conf->verify()) { conf.reset(); return {}; diff --git a/usr.sbin/ctld/ctld.hh b/usr.sbin/ctld/ctld.hh index 6ecee3b73c4f..a5aab65e339b 100644 --- a/usr.sbin/ctld/ctld.hh +++ b/usr.sbin/ctld/ctld.hh @@ -110,6 +110,12 @@ struct auth_group { const char *user2, const char *secret2); const struct auth *find_auth(std::string_view user) const; + bool add_host_nqn(std::string_view nqn); + bool host_permitted(std::string_view nqn) const; + + bool add_host_address(const char *address); + bool host_permitted(const struct sockaddr *sa) const; + bool add_initiator_name(std::string_view initiator_name); bool initiator_permitted(std::string_view initiator_name) const; @@ -123,6 +129,8 @@ private: std::string ag_label; auth_type ag_type = auth_type::UNKNOWN; std::unordered_map<std::string, auth> ag_auths; + std::unordered_set<std::string> ag_host_names; + std::list<auth_portal> ag_host_addresses; std::unordered_set<std::string> ag_initiator_names; std::list<auth_portal> ag_initiator_portals; }; @@ -131,7 +139,9 @@ using auth_group_sp = std::shared_ptr<auth_group>; enum class portal_protocol { ISCSI, - ISER + ISER, + NVME_TCP, + NVME_DISCOVERY_TCP, }; struct portal { @@ -147,7 +157,7 @@ struct portal { virtual void handle_connection(freebsd::fd_up fd, const char *host, const struct sockaddr *client_sa) = 0; - portal_group *portal_group() { return p_portal_group; } + portal_group *portal_group() const { return p_portal_group; } const char *listen() const { return p_listen.c_str(); } const addrinfo *ai() const { return p_ai.get(); } portal_protocol protocol() const { return p_protocol; } @@ -386,9 +396,12 @@ struct target { bool add_chap(const char *user, const char *secret); bool add_chap_mutual(const char *user, const char *secret, const char *user2, const char *secret2); + virtual bool add_host_address(const char *) { return false; } + virtual bool add_host_nqn(std::string_view) { return false; } virtual bool add_initiator_name(std::string_view) { return false; } virtual bool add_initiator_portal(const char *) { return false; } virtual bool add_lun(u_int, const char *) { return false; } + virtual bool add_namespace(u_int, const char *) { return false; } virtual bool add_portal_group(const char *pg_name, const char *ag_name) = 0; bool set_alias(std::string_view alias); @@ -397,6 +410,7 @@ struct target { bool set_physical_port(std::string_view pport); bool set_redirection(const char *addr); virtual struct lun *start_lun(u_int) { return nullptr; } + virtual struct lun *start_namespace(u_int) { return nullptr; } void add_port(struct port *port); void remove_lun(struct lun *lun); @@ -440,13 +454,18 @@ private: }; struct conf { + conf(); + int maxproc() const { return conf_maxproc; } int timeout() const { return conf_timeout; } + uint32_t genctr() const { return conf_genctr; } bool default_auth_group_defined() const { return conf_default_ag_defined; } bool default_portal_group_defined() const { return conf_default_pg_defined; } + bool default_transport_group_defined() const + { return conf_default_tg_defined; } struct auth_group *add_auth_group(const char *ag_name); struct auth_group *define_default_auth_group(); @@ -456,6 +475,10 @@ struct conf { struct portal_group *define_default_portal_group(); struct portal_group *find_portal_group(std::string_view name); + struct portal_group *add_transport_group(const char *name); + struct portal_group *define_default_transport_group(); + struct portal_group *find_transport_group(std::string_view name); + bool add_port(struct target *target, struct portal_group *pg, auth_group_sp ag); bool add_port(struct target *target, struct portal_group *pg, @@ -465,6 +488,9 @@ struct conf { int vp); bool add_pports(struct kports &kports); + struct target *add_controller(const char *name); + struct target *find_controller(std::string_view name); + struct target *add_target(const char *name); struct target *find_target(std::string_view name); @@ -501,10 +527,12 @@ private: std::string conf_pidfile_path; std::unordered_map<std::string, std::unique_ptr<lun>> conf_luns; - std::unordered_map<std::string, std::unique_ptr<target>> conf_targets; + std::unordered_map<std::string, target_up> conf_targets; + std::unordered_map<std::string, target_up> conf_controllers; std::unordered_map<std::string, auth_group_sp> conf_auth_groups; std::unordered_map<std::string, std::unique_ptr<port>> conf_ports; std::unordered_map<std::string, portal_group_up> conf_portal_groups; + std::unordered_map<std::string, portal_group_up> conf_transport_groups; std::unordered_map<std::string, isns> conf_isns; struct target *conf_first_target = nullptr; int conf_isns_period = 900; @@ -512,12 +540,16 @@ private: int conf_debug = 0; int conf_timeout = 60; int conf_maxproc = 30; + uint32_t conf_genctr = 0; freebsd::pidfile conf_pidfile; bool conf_default_pg_defined = false; + bool conf_default_tg_defined = false; bool conf_default_ag_defined = false; + static uint32_t global_genctr; + #ifdef ICL_KERNEL_PROXY public: int add_proxy_portal(portal *); @@ -593,6 +625,11 @@ portal_group_up iscsi_make_portal_group(struct conf *conf, target_up iscsi_make_target(struct conf *conf, std::string_view name); +portal_group_up nvmf_make_transport_group(struct conf *conf, + std::string_view name); +target_up nvmf_make_controller(struct conf *conf, + std::string_view name); + void start_timer(int timeout, bool fatal = false); void stop_timer(); *** 1962 LINES SKIPPED ***