diff options
Diffstat (limited to 'usr.sbin/ctld/ctld.hh')
-rw-r--r-- | usr.sbin/ctld/ctld.hh | 638 |
1 files changed, 638 insertions, 0 deletions
diff --git a/usr.sbin/ctld/ctld.hh b/usr.sbin/ctld/ctld.hh new file mode 100644 index 000000000000..3bf18f6a32c0 --- /dev/null +++ b/usr.sbin/ctld/ctld.hh @@ -0,0 +1,638 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2012 The FreeBSD Foundation + * + * 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. + */ + +#ifndef __CTLD_HH__ +#define __CTLD_HH__ + +#include <sys/_nv.h> +#include <sys/queue.h> +#ifdef ICL_KERNEL_PROXY +#include <sys/types.h> +#endif +#include <sys/socket.h> +#include <stdbool.h> +#include <libiscsiutil.h> +#include <libutil.h> + +#include <array> +#include <list> +#include <memory> +#include <string> +#include <string_view> +#include <unordered_map> +#include <unordered_set> +#include <libutil++.hh> + +#define DEFAULT_CONFIG_PATH "/etc/ctl.conf" +#define DEFAULT_PIDFILE "/var/run/ctld.pid" +#define DEFAULT_BLOCKSIZE 512 +#define DEFAULT_CD_BLOCKSIZE 2048 + +#define MAX_LUNS 1024 + +struct isns_req; +struct port; + +struct auth { + auth(std::string_view secret) : a_secret(secret) {} + auth(std::string_view secret, std::string_view mutual_user, + std::string_view mutual_secret) : + a_secret(secret), a_mutual_user(mutual_user), + a_mutual_secret(mutual_secret) {} + + bool mutual() const { return !a_mutual_user.empty(); } + + const char *secret() const { return a_secret.c_str(); } + const char *mutual_user() const { return a_mutual_user.c_str(); } + const char *mutual_secret() const { return a_mutual_secret.c_str(); } + +private: + std::string a_secret; + std::string a_mutual_user; + std::string a_mutual_secret; +}; + +struct auth_portal { + bool matches(const struct sockaddr *sa) const; + bool parse(const char *portal); + +private: + struct sockaddr_storage ap_sa; + int ap_mask = 0; +}; + +enum class auth_type { + UNKNOWN, + DENY, + NO_AUTHENTICATION, + CHAP, + CHAP_MUTUAL +}; + +struct auth_group { + auth_group(std::string label) : ag_label(label) {} + + auth_type type() const { return ag_type; } + bool set_type(const char *str); + void set_type(auth_type type); + + const char *label() const { return ag_label.c_str(); } + + 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); + 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; + + bool add_initiator_portal(const char *initiator_portal); + bool initiator_permitted(const struct sockaddr *sa) const; + +private: + void check_secret_length(const char *user, const char *secret, + const char *secret_type); + + 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; +}; + +using auth_group_sp = std::shared_ptr<auth_group>; + +enum class portal_protocol { + ISCSI, + ISER, + NVME_TCP, + NVME_DISCOVERY_TCP, +}; + +struct portal { + portal(struct portal_group *pg, std::string_view listen, + portal_protocol protocol, freebsd::addrinfo_up ai) : + p_portal_group(pg), p_listen(listen), p_ai(std::move(ai)), + p_protocol(protocol) {} + virtual ~portal() = default; + + virtual bool prepare() { return true; } + bool reuse_socket(portal &oldp); + bool init_socket(); + virtual bool init_socket_options(int s __unused) { return true; } + virtual void handle_connection(freebsd::fd_up fd, const char *host, + const struct sockaddr *client_sa) = 0; + + struct 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; } + int socket() const { return p_socket; } + void close() { p_socket.reset(); } + +private: + struct portal_group *p_portal_group; + std::string p_listen; + freebsd::addrinfo_up p_ai; + portal_protocol p_protocol; + + freebsd::fd_up p_socket; +}; + +using portal_up = std::unique_ptr<portal>; +using port_up = std::unique_ptr<port>; + +enum class discovery_filter { + UNKNOWN, + NONE, + PORTAL, + PORTAL_NAME, + PORTAL_NAME_AUTH +}; + +struct portal_group { + portal_group(struct conf *conf, std::string_view name); + virtual ~portal_group() = default; + + struct conf *conf() const { return pg_conf; } + virtual const char *keyword() const = 0; + const char *name() const { return pg_name.c_str(); } + bool assigned() const { return pg_assigned; } + bool is_dummy() const; + bool is_redirecting() const { return !pg_redirection.empty(); } + struct auth_group *discovery_auth_group() const + { return pg_discovery_auth_group.get(); } + enum discovery_filter discovery_filter() const + { return pg_discovery_filter; } + int dscp() const { return pg_dscp; } + const char *offload() const { return pg_offload.c_str(); } + const char *redirection() const { return pg_redirection.c_str(); } + int pcp() const { return pg_pcp; } + uint16_t tag() const { return pg_tag; } + + freebsd::nvlist_up options() const; + + const std::list<portal_up> &portals() const { return pg_portals; } + const std::unordered_map<std::string, port *> &ports() const + { return pg_ports; } + + virtual void allocate_tag() = 0; + virtual bool add_portal(const char *value, + portal_protocol protocol) = 0; + virtual void add_default_portals() = 0; + bool add_option(const char *name, const char *value); + bool set_discovery_auth_group(const char *name); + bool set_dscp(u_int dscp); + virtual bool set_filter(const char *str) = 0; + void set_foreign(); + bool set_offload(const char *offload); + bool set_pcp(u_int pcp); + bool set_redirection(const char *addr); + void set_tag(uint16_t tag); + + virtual port_up create_port(struct target *target, auth_group_sp ag) = + 0; + virtual port_up create_port(struct target *target, uint32_t ctl_port) = + 0; + + void add_port(struct portal_group_port *port); + const struct port *find_port(std::string_view target) const; + void remove_port(struct portal_group_port *port); + void verify(struct conf *conf); + + bool reuse_socket(struct portal &newp); + int open_sockets(struct conf &oldconf); + void close_sockets(); + +protected: + struct conf *pg_conf; + freebsd::nvlist_up pg_options; + const char *pg_keyword; + std::string pg_name; + auth_group_sp pg_discovery_auth_group; + enum discovery_filter pg_discovery_filter = + discovery_filter::UNKNOWN; + bool pg_foreign = false; + bool pg_assigned = false; + std::list<portal_up> pg_portals; + std::unordered_map<std::string, port *> pg_ports; + std::string pg_offload; + std::string pg_redirection; + int pg_dscp = -1; + int pg_pcp = -1; + + uint16_t pg_tag = 0; +}; + +using portal_group_up = std::unique_ptr<portal_group>; + +struct port { + port(struct target *target); + virtual ~port() = default; + + struct target *target() const { return p_target; } + virtual struct auth_group *auth_group() const { return nullptr; } + virtual struct portal_group *portal_group() const { return nullptr; } + + virtual bool is_dummy() const { return true; } + + virtual void clear_references(); + + bool kernel_add(); + bool kernel_update(const port *oport); + bool kernel_remove(); + + virtual bool kernel_create_port() = 0; + virtual bool kernel_remove_port() = 0; + +protected: + struct target *p_target; + + uint32_t p_ctl_port = 0; +}; + +struct portal_group_port : public port { + portal_group_port(struct target *target, struct portal_group *pg, + auth_group_sp ag); + portal_group_port(struct target *target, struct portal_group *pg, + uint32_t ctl_port); + ~portal_group_port() override = default; + + struct auth_group *auth_group() const override + { return p_auth_group.get(); } + struct portal_group *portal_group() const override + { return p_portal_group; } + + bool is_dummy() const override; + + void clear_references() override; + +protected: + auth_group_sp p_auth_group; + struct portal_group *p_portal_group; +}; + +struct ioctl_port final : public port { + ioctl_port(struct target *target, int pp, int vp) : + port(target), p_ioctl_pp(pp), p_ioctl_vp(vp) {} + ~ioctl_port() override = default; + + bool kernel_create_port() override; + bool kernel_remove_port() override; + +private: + int p_ioctl_pp; + int p_ioctl_vp; +}; + +struct kernel_port final : public port { + kernel_port(struct target *target, struct pport *pp) : + port(target), p_pport(pp) {} + ~kernel_port() override = default; + + bool kernel_create_port() override; + bool kernel_remove_port() override; + +private: + struct pport *p_pport; +}; + +struct lun { + lun(struct conf *conf, std::string_view name); + + const char *name() const { return l_name.c_str(); } + const std::string &path() const { return l_path; } + int ctl_lun() const { return l_ctl_lun; } + + freebsd::nvlist_up options() const; + + bool add_option(const char *name, const char *value); + bool set_backend(std::string_view value); + bool set_blocksize(size_t value); + bool set_ctl_lun(uint32_t value); + bool set_device_type(uint8_t device_type); + bool set_device_type(const char *value); + bool set_device_id(std::string_view value); + bool set_path(std::string_view value); + void set_scsiname(std::string_view value); + bool set_serial(std::string_view value); + bool set_size(uint64_t value); + + bool changed(const struct lun &old) const; + bool verify(); + + bool kernel_add(); + bool kernel_modify() const; + bool kernel_remove() const; + +private: + struct conf *l_conf; + freebsd::nvlist_up l_options; + std::string l_name; + std::string l_backend; + uint8_t l_device_type = 0; + int l_blocksize = 0; + std::string l_device_id; + std::string l_path; + std::string l_scsiname; + std::string l_serial; + uint64_t l_size = 0; + + int l_ctl_lun = -1; +}; + +struct target { + target(struct conf *conf, const char *keyword, std::string_view name); + virtual ~target() = default; + + bool has_alias() const { return !t_alias.empty(); } + bool has_pport() const { return !t_pport.empty(); } + bool has_redirection() const { return !t_redirection.empty(); } + const char *alias() const { return t_alias.c_str(); } + const char *name() const { return t_name.c_str(); } + const char *label() const { return t_label.c_str(); } + const char *pport() const { return t_pport.c_str(); } + bool private_auth() const { return t_private_auth; } + const char *redirection() const { return t_redirection.c_str(); } + + struct auth_group *auth_group() const { return t_auth_group.get(); } + const std::list<port *> &ports() const { return t_ports; } + const struct lun *lun(int idx) const { return t_luns[idx]; } + + 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); + bool set_auth_group(const char *ag_name); + bool set_auth_type(const char *type); + 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); + void remove_port(struct port *port); + void verify(); + +protected: + bool use_private_auth(const char *keyword); + bool add_lun(u_int id, const char *lun_label, const char *lun_name); + struct lun *start_lun(u_int id, const char *lun_label, + const char *lun_name); + virtual struct portal_group *default_portal_group() = 0; + + struct conf *t_conf; + std::array<struct lun *, MAX_LUNS> t_luns = {}; + auth_group_sp t_auth_group; + std::list<port *> t_ports; + std::string t_name; + std::string t_label; + std::string t_alias; + std::string t_redirection; + /* Name of this target's physical port, if any, i.e. "isp0" */ + std::string t_pport; + bool t_private_auth = false; +}; + +using target_up = std::unique_ptr<target>; + +struct isns { + isns(std::string_view addr, freebsd::addrinfo_up ai) : + i_addr(addr), i_ai(std::move(ai)) {} + + const char *addr() const { return i_addr.c_str(); } + + freebsd::fd_up connect(); + bool send_request(int s, struct isns_req req); + +private: + std::string i_addr; + freebsd::addrinfo_up i_ai; +}; + +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(); + auth_group_sp find_auth_group(std::string_view ag_name); + + struct portal_group *add_portal_group(const char *name); + 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, + uint32_t ctl_port); + bool add_port(struct target *target, struct pport *pp); + bool add_port(struct kports &kports, struct target *target, int pp, + 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); + + struct lun *add_lun(const char *name); + struct lun *find_lun(std::string_view name); + + void set_debug(int debug); + void set_isns_period(int period); + void set_isns_timeout(int timeout); + void set_maxproc(int maxproc); + bool set_pidfile_path(std::string_view path); + void set_timeout(int timeout); + + void open_pidfile(); + void write_pidfile(); + void close_pidfile(); + + bool add_isns(const char *addr); + void isns_register_targets(struct isns *isns, struct conf *oldconf); + void isns_deregister_targets(struct isns *isns); + void isns_schedule_update(); + void isns_update(); + + int apply(struct conf *oldconf); + void delete_target_luns(struct lun *lun); + bool reuse_portal_group_socket(struct portal &newp); + bool verify(); + +private: + struct isns_req isns_register_request(const char *hostname); + struct isns_req isns_check_request(const char *hostname); + struct isns_req isns_deregister_request(const char *hostname); + void isns_check(struct isns *isns); + + std::string conf_pidfile_path; + std::unordered_map<std::string, std::unique_ptr<lun>> conf_luns; + 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; + int conf_isns_timeout = 5; + 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 *); + portal *proxy_portal(int); +private: + std::vector<portal *> conf_proxy_portals; +#endif +}; + +using conf_up = std::unique_ptr<conf>; + +/* Physical ports exposed by the kernel */ +struct pport { + pport(std::string_view name, uint32_t ctl_port) : pp_name(name), + pp_ctl_port(ctl_port) {} + + const char *name() const { return pp_name.c_str(); } + uint32_t ctl_port() const { return pp_ctl_port; } + + bool linked() const { return pp_linked; } + void link() { pp_linked = true; } + +private: + std::string pp_name; + uint32_t pp_ctl_port; + bool pp_linked = false; +}; + +struct kports { + bool add_port(std::string &name, uint32_t ctl_port); + bool has_port(std::string_view name); + struct pport *find_port(std::string_view name); + +private: + std::unordered_map<std::string, struct pport> pports; +}; + +extern bool proxy_mode; +extern int ctl_fd; + +bool parse_conf(const char *path); +bool uclparse_conf(const char *path); + +conf_up conf_new_from_kernel(struct kports &kports); +void conf_finish(void); +void conf_start(struct conf *new_conf); + +bool option_new(nvlist_t *nvl, + const char *name, const char *value); + +freebsd::addrinfo_up parse_addr_port(const char *address, + const char *def_port); + +void kernel_init(void); +void kernel_capsicate(void); + +#ifdef ICL_KERNEL_PROXY +void kernel_listen(struct addrinfo *ai, bool iser, + int portal_id); +void kernel_accept(int *connection_id, int *portal_id, + struct sockaddr *client_sa, + socklen_t *client_salen); +void kernel_send(struct pdu *pdu); +void kernel_receive(struct pdu *pdu); +#endif + +bool ctl_create_port(const char *driver, + const nvlist_t *nvl, uint32_t *ctl_port); +bool ctl_remove_port(const char *driver, nvlist_t *nvl); + +portal_group_up iscsi_make_portal_group(struct conf *conf, + std::string_view name); +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(); +bool timed_out(); + +#endif /* !__CTLD_HH__ */ |