mirror of
https://github.com/opnsense/src.git
synced 2026-06-11 09:41:03 -04:00
Use C++ STL containers (std::string, std::list) for the data structure holding the parsed output of the XML configuration obtained from the kernel. This simplifies the code in various places and removes the need for manual memory freeing (which was incomplete). Use a std::list of pairs of std::string objects for the port and LUN attribute lists instead of an nvlist. Use a std::vector<char> for the resizable buffer receiving XML results via ioctl(). Also, reuse the buffer from CTL_LUN_LIST for CTL_PORT_LIST rather than doing a free() only to turn around and malloc() again. While here, split out the code for fetching and parsing the XML into a separate function. Sponsored by: Chelsio Communications Pull Request: https://github.com/freebsd/freebsd-src/pull/1794
1161 lines
30 KiB
C++
1161 lines
30 KiB
C++
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2003, 2004 Silicon Graphics International Corp.
|
|
* Copyright (c) 1997-2007 Kenneth D. Merry
|
|
* Copyright (c) 2012 The FreeBSD Foundation
|
|
* Copyright (c) 2017 Jakub Wojciech Klama <jceel@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Portions of this software were 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,
|
|
* without modification.
|
|
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
|
* substantially similar to the "NO WARRANTY" disclaimer below
|
|
* ("Disclaimer") and any redistribution must be conditioned upon
|
|
* including a substantially similar Disclaimer requirement for further
|
|
* binary redistribution.
|
|
*
|
|
* NO WARRANTY
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
|
|
*
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/capsicum.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/cnv.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/linker.h>
|
|
#include <sys/module.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/sbuf.h>
|
|
#include <sys/stat.h>
|
|
#include <assert.h>
|
|
#include <bsdxml.h>
|
|
#include <capsicum_helpers.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <cam/scsi/scsi_all.h>
|
|
#include <cam/scsi/scsi_message.h>
|
|
#include <cam/ctl/ctl.h>
|
|
#include <cam/ctl/ctl_io.h>
|
|
#include <cam/ctl/ctl_backend.h>
|
|
#include <cam/ctl/ctl_ioctl.h>
|
|
#include <cam/ctl/ctl_util.h>
|
|
#include <cam/ctl/ctl_scsi_all.h>
|
|
|
|
#include "ctld.hh"
|
|
|
|
#ifdef ICL_KERNEL_PROXY
|
|
#include <netdb.h>
|
|
#endif
|
|
|
|
#define NVLIST_BUFSIZE 1024
|
|
|
|
extern bool proxy_mode;
|
|
|
|
int ctl_fd = 0;
|
|
|
|
void
|
|
kernel_init(void)
|
|
{
|
|
int retval, saved_errno;
|
|
|
|
ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
|
|
if (ctl_fd < 0 && errno == ENOENT) {
|
|
saved_errno = errno;
|
|
retval = kldload("ctl");
|
|
if (retval != -1)
|
|
ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
|
|
else
|
|
errno = saved_errno;
|
|
}
|
|
if (ctl_fd < 0)
|
|
log_err(1, "failed to open %s", CTL_DEFAULT_DEV);
|
|
#ifdef WANT_ISCSI
|
|
else {
|
|
saved_errno = errno;
|
|
if (modfind("cfiscsi") == -1 && kldload("cfiscsi") == -1)
|
|
log_warn("couldn't load cfiscsi");
|
|
errno = saved_errno;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Backend LUN information.
|
|
*/
|
|
using attr_list = std::list<std::pair<std::string, std::string>>;
|
|
|
|
struct cctl_lun {
|
|
uint64_t lun_id;
|
|
std::string backend_type;
|
|
uint8_t device_type;
|
|
uint64_t size_blocks;
|
|
uint32_t blocksize;
|
|
std::string serial_number;
|
|
std::string device_id;
|
|
std::string ctld_name;
|
|
attr_list attr_list;
|
|
};
|
|
|
|
struct cctl_port {
|
|
uint32_t port_id;
|
|
std::string port_frontend;
|
|
std::string port_name;
|
|
int pp;
|
|
int vp;
|
|
int cfiscsi_state;
|
|
std::string cfiscsi_target;
|
|
uint16_t cfiscsi_portal_group_tag;
|
|
std::string ctld_portal_group_name;
|
|
attr_list attr_list;
|
|
};
|
|
|
|
struct cctl_devlist_data {
|
|
std::list<cctl_lun> lun_list;
|
|
struct cctl_lun *cur_lun = nullptr;
|
|
std::list<cctl_port> port_list;
|
|
struct cctl_port *cur_port = nullptr;
|
|
u_int level = 0;
|
|
struct sbuf *cur_sb[32] = {};
|
|
};
|
|
|
|
static void
|
|
cctl_start_element(void *user_data, const char *name, const char **attr)
|
|
{
|
|
int i;
|
|
struct cctl_devlist_data *devlist;
|
|
struct cctl_lun *cur_lun;
|
|
|
|
devlist = (struct cctl_devlist_data *)user_data;
|
|
cur_lun = devlist->cur_lun;
|
|
devlist->level++;
|
|
if (devlist->level >= nitems(devlist->cur_sb))
|
|
log_errx(1, "%s: too many nesting levels, %zu max", __func__,
|
|
nitems(devlist->cur_sb));
|
|
|
|
devlist->cur_sb[devlist->level] = sbuf_new_auto();
|
|
if (devlist->cur_sb[devlist->level] == NULL)
|
|
log_err(1, "%s: unable to allocate sbuf", __func__);
|
|
|
|
if (strcmp(name, "lun") == 0) {
|
|
if (cur_lun != NULL)
|
|
log_errx(1, "%s: improper lun element nesting",
|
|
__func__);
|
|
|
|
devlist->lun_list.emplace_back();
|
|
cur_lun = &devlist->lun_list.back();
|
|
|
|
devlist->cur_lun = cur_lun;
|
|
|
|
for (i = 0; attr[i] != NULL; i += 2) {
|
|
if (strcmp(attr[i], "id") == 0) {
|
|
cur_lun->lun_id = strtoull(attr[i+1], NULL, 0);
|
|
} else {
|
|
log_errx(1, "%s: invalid LUN attribute %s = %s",
|
|
__func__, attr[i], attr[i+1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
cctl_end_element(void *user_data, const char *name)
|
|
{
|
|
struct cctl_devlist_data *devlist;
|
|
struct cctl_lun *cur_lun;
|
|
std::string str;
|
|
|
|
devlist = (struct cctl_devlist_data *)user_data;
|
|
cur_lun = devlist->cur_lun;
|
|
|
|
if ((cur_lun == NULL)
|
|
&& (strcmp(name, "ctllunlist") != 0))
|
|
log_errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name);
|
|
|
|
if (devlist->cur_sb[devlist->level] == NULL)
|
|
log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
|
|
devlist->level, name);
|
|
|
|
sbuf_finish(devlist->cur_sb[devlist->level]);
|
|
str = sbuf_data(devlist->cur_sb[devlist->level]);
|
|
|
|
sbuf_delete(devlist->cur_sb[devlist->level]);
|
|
devlist->cur_sb[devlist->level] = NULL;
|
|
devlist->level--;
|
|
|
|
if (strcmp(name, "backend_type") == 0) {
|
|
cur_lun->backend_type = std::move(str);
|
|
} else if (strcmp(name, "lun_type") == 0) {
|
|
if (str.empty())
|
|
log_errx(1, "%s: %s missing its argument", __func__, name);
|
|
cur_lun->device_type = strtoull(str.c_str(), NULL, 0);
|
|
} else if (strcmp(name, "size") == 0) {
|
|
if (str.empty())
|
|
log_errx(1, "%s: %s missing its argument", __func__, name);
|
|
cur_lun->size_blocks = strtoull(str.c_str(), NULL, 0);
|
|
} else if (strcmp(name, "blocksize") == 0) {
|
|
if (str.empty())
|
|
log_errx(1, "%s: %s missing its argument", __func__, name);
|
|
cur_lun->blocksize = strtoul(str.c_str(), NULL, 0);
|
|
} else if (strcmp(name, "serial_number") == 0) {
|
|
cur_lun->serial_number = std::move(str);
|
|
} else if (strcmp(name, "device_id") == 0) {
|
|
cur_lun->device_id = std::move(str);
|
|
} else if (strcmp(name, "ctld_name") == 0) {
|
|
cur_lun->ctld_name = std::move(str);
|
|
} else if (strcmp(name, "lun") == 0) {
|
|
devlist->cur_lun = NULL;
|
|
} else if (strcmp(name, "ctllunlist") == 0) {
|
|
/* Nothing. */
|
|
} else {
|
|
cur_lun->attr_list.emplace_back(name, std::move(str));
|
|
}
|
|
}
|
|
|
|
static void
|
|
cctl_start_pelement(void *user_data, const char *name, const char **attr)
|
|
{
|
|
int i;
|
|
struct cctl_devlist_data *devlist;
|
|
struct cctl_port *cur_port;
|
|
|
|
devlist = (struct cctl_devlist_data *)user_data;
|
|
cur_port = devlist->cur_port;
|
|
devlist->level++;
|
|
if (devlist->level >= nitems(devlist->cur_sb))
|
|
log_errx(1, "%s: too many nesting levels, %zu max", __func__,
|
|
nitems(devlist->cur_sb));
|
|
|
|
devlist->cur_sb[devlist->level] = sbuf_new_auto();
|
|
if (devlist->cur_sb[devlist->level] == NULL)
|
|
log_err(1, "%s: unable to allocate sbuf", __func__);
|
|
|
|
if (strcmp(name, "targ_port") == 0) {
|
|
if (cur_port != NULL)
|
|
log_errx(1, "%s: improper port element nesting (%s)",
|
|
__func__, name);
|
|
|
|
devlist->port_list.emplace_back();
|
|
cur_port = &devlist->port_list.back();
|
|
devlist->cur_port = cur_port;
|
|
|
|
for (i = 0; attr[i] != NULL; i += 2) {
|
|
if (strcmp(attr[i], "id") == 0) {
|
|
cur_port->port_id = strtoul(attr[i+1], NULL, 0);
|
|
} else {
|
|
log_errx(1, "%s: invalid LUN attribute %s = %s",
|
|
__func__, attr[i], attr[i+1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
cctl_end_pelement(void *user_data, const char *name)
|
|
{
|
|
struct cctl_devlist_data *devlist;
|
|
struct cctl_port *cur_port;
|
|
std::string str;
|
|
|
|
devlist = (struct cctl_devlist_data *)user_data;
|
|
cur_port = devlist->cur_port;
|
|
|
|
if ((cur_port == NULL)
|
|
&& (strcmp(name, "ctlportlist") != 0))
|
|
log_errx(1, "%s: cur_port == NULL! (name = %s)", __func__, name);
|
|
|
|
if (devlist->cur_sb[devlist->level] == NULL)
|
|
log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
|
|
devlist->level, name);
|
|
|
|
sbuf_finish(devlist->cur_sb[devlist->level]);
|
|
str = sbuf_data(devlist->cur_sb[devlist->level]);
|
|
|
|
sbuf_delete(devlist->cur_sb[devlist->level]);
|
|
devlist->cur_sb[devlist->level] = NULL;
|
|
devlist->level--;
|
|
|
|
if (strcmp(name, "frontend_type") == 0) {
|
|
cur_port->port_frontend = std::move(str);
|
|
} else if (strcmp(name, "port_name") == 0) {
|
|
cur_port->port_name = std::move(str);
|
|
} else if (strcmp(name, "physical_port") == 0) {
|
|
if (str.empty())
|
|
log_errx(1, "%s: %s missing its argument", __func__, name);
|
|
cur_port->pp = strtoul(str.c_str(), NULL, 0);
|
|
} else if (strcmp(name, "virtual_port") == 0) {
|
|
if (str.empty())
|
|
log_errx(1, "%s: %s missing its argument", __func__, name);
|
|
cur_port->vp = strtoul(str.c_str(), NULL, 0);
|
|
} else if (strcmp(name, "cfiscsi_target") == 0) {
|
|
cur_port->cfiscsi_target = std::move(str);
|
|
} else if (strcmp(name, "cfiscsi_state") == 0) {
|
|
if (str.empty())
|
|
log_errx(1, "%s: %s missing its argument", __func__, name);
|
|
cur_port->cfiscsi_state = strtoul(str.c_str(), NULL, 0);
|
|
} else if (strcmp(name, "cfiscsi_portal_group_tag") == 0) {
|
|
if (str.empty())
|
|
log_errx(1, "%s: %s missing its argument", __func__, name);
|
|
cur_port->cfiscsi_portal_group_tag = strtoul(str.c_str(), NULL, 0);
|
|
} else if (strcmp(name, "ctld_portal_group_name") == 0) {
|
|
cur_port->ctld_portal_group_name = std::move(str);
|
|
} else if (strcmp(name, "targ_port") == 0) {
|
|
devlist->cur_port = NULL;
|
|
} else if (strcmp(name, "ctlportlist") == 0) {
|
|
/* Nothing. */
|
|
} else {
|
|
cur_port->attr_list.emplace_back(name, std::move(str));
|
|
}
|
|
}
|
|
|
|
static void
|
|
cctl_char_handler(void *user_data, const XML_Char *str, int len)
|
|
{
|
|
struct cctl_devlist_data *devlist;
|
|
|
|
devlist = (struct cctl_devlist_data *)user_data;
|
|
|
|
sbuf_bcat(devlist->cur_sb[devlist->level], str, len);
|
|
}
|
|
|
|
static bool
|
|
parse_kernel_config(struct cctl_devlist_data &devlist)
|
|
{
|
|
struct ctl_lun_list list;
|
|
XML_Parser parser;
|
|
int retval;
|
|
|
|
std::vector<char> buf(4096);
|
|
retry:
|
|
bzero(&list, sizeof(list));
|
|
list.alloc_len = buf.size();
|
|
list.status = CTL_LUN_LIST_NONE;
|
|
list.lun_xml = buf.data();
|
|
|
|
if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) {
|
|
log_warn("error issuing CTL_LUN_LIST ioctl");
|
|
return (false);
|
|
}
|
|
|
|
if (list.status == CTL_LUN_LIST_ERROR) {
|
|
log_warnx("error returned from CTL_LUN_LIST ioctl: %s",
|
|
list.error_str);
|
|
return (false);
|
|
}
|
|
|
|
if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
|
|
buf.resize(buf.size() << 1);
|
|
goto retry;
|
|
}
|
|
|
|
parser = XML_ParserCreate(NULL);
|
|
if (parser == NULL) {
|
|
log_warnx("unable to create XML parser");
|
|
return (false);
|
|
}
|
|
|
|
XML_SetUserData(parser, &devlist);
|
|
XML_SetElementHandler(parser, cctl_start_element, cctl_end_element);
|
|
XML_SetCharacterDataHandler(parser, cctl_char_handler);
|
|
|
|
retval = XML_Parse(parser, buf.data(), strlen(buf.data()), 1);
|
|
XML_ParserFree(parser);
|
|
if (retval != 1) {
|
|
log_warnx("XML_Parse failed");
|
|
return (false);
|
|
}
|
|
|
|
retry_port:
|
|
bzero(&list, sizeof(list));
|
|
list.alloc_len = buf.size();
|
|
list.status = CTL_LUN_LIST_NONE;
|
|
list.lun_xml = buf.data();
|
|
|
|
if (ioctl(ctl_fd, CTL_PORT_LIST, &list) == -1) {
|
|
log_warn("error issuing CTL_PORT_LIST ioctl");
|
|
return (false);
|
|
}
|
|
|
|
if (list.status == CTL_LUN_LIST_ERROR) {
|
|
log_warnx("error returned from CTL_PORT_LIST ioctl: %s",
|
|
list.error_str);
|
|
return (false);
|
|
}
|
|
|
|
if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
|
|
buf.resize(buf.size() << 1);
|
|
goto retry_port;
|
|
}
|
|
|
|
parser = XML_ParserCreate(NULL);
|
|
if (parser == NULL) {
|
|
log_warnx("unable to create XML parser");
|
|
return (false);
|
|
}
|
|
|
|
XML_SetUserData(parser, &devlist);
|
|
XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement);
|
|
XML_SetCharacterDataHandler(parser, cctl_char_handler);
|
|
|
|
retval = XML_Parse(parser, buf.data(), strlen(buf.data()), 1);
|
|
XML_ParserFree(parser);
|
|
if (retval != 1) {
|
|
log_warnx("XML_Parse failed");
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
conf_up
|
|
conf_new_from_kernel(struct kports &kports)
|
|
{
|
|
struct cctl_devlist_data devlist;
|
|
|
|
log_debugx("obtaining previously configured CTL luns from the kernel");
|
|
|
|
if (!parse_kernel_config(devlist))
|
|
return {};
|
|
|
|
conf_up conf = std::make_unique<struct conf>();
|
|
|
|
for (const auto &port : devlist.port_list) {
|
|
if (port.port_frontend == "ha")
|
|
continue;
|
|
|
|
std::string name = port.port_name;
|
|
if (port.pp != 0) {
|
|
name += "/" + std::to_string(port.pp);
|
|
if (port.vp != 0)
|
|
name += "/" + std::to_string(port.vp);
|
|
}
|
|
|
|
if (port.cfiscsi_target.empty()) {
|
|
log_debugx("CTL port %u \"%s\" wasn't managed by ctld; ",
|
|
port.port_id, name.c_str());
|
|
if (!kports.has_port(name)) {
|
|
if (!kports.add_port(name, port.port_id)) {
|
|
log_warnx("kports::add_port failed");
|
|
continue;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if (port.cfiscsi_state != 1) {
|
|
log_debugx("CTL port %ju is not active (%d); ignoring",
|
|
(uintmax_t)port.port_id, port.cfiscsi_state);
|
|
continue;
|
|
}
|
|
|
|
const char *t_name = port.cfiscsi_target.c_str();
|
|
struct target *targ = conf->find_target(t_name);
|
|
if (targ == NULL) {
|
|
targ = conf->add_target(t_name);
|
|
if (targ == NULL) {
|
|
log_warnx("Failed to add target \"%s\"",
|
|
t_name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (port.ctld_portal_group_name.empty())
|
|
continue;
|
|
const char *pg_name = port.ctld_portal_group_name.c_str();
|
|
struct portal_group *pg = conf->find_portal_group(pg_name);
|
|
if (pg == NULL) {
|
|
pg = conf->add_portal_group(pg_name);
|
|
if (pg == NULL) {
|
|
log_warnx("Failed to add portal_group \"%s\"",
|
|
pg_name);
|
|
continue;
|
|
}
|
|
}
|
|
pg->set_tag(port.cfiscsi_portal_group_tag);
|
|
if (!conf->add_port(targ, pg, port.port_id)) {
|
|
log_warnx("Failed to add port for target \"%s\" and portal-group \"%s\"",
|
|
t_name, pg_name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
for (const auto &lun : devlist.lun_list) {
|
|
if (lun.ctld_name.empty()) {
|
|
log_debugx("CTL lun %ju wasn't managed by ctld; "
|
|
"ignoring", (uintmax_t)lun.lun_id);
|
|
continue;
|
|
}
|
|
|
|
const char *l_name = lun.ctld_name.c_str();
|
|
struct lun *cl = conf->find_lun(l_name);
|
|
if (cl != NULL) {
|
|
log_warnx("found CTL lun %ju \"%s\", "
|
|
"also backed by CTL lun %d; ignoring",
|
|
(uintmax_t)lun.lun_id, l_name,
|
|
cl->ctl_lun());
|
|
continue;
|
|
}
|
|
|
|
log_debugx("found CTL lun %ju \"%s\"",
|
|
(uintmax_t)lun.lun_id, l_name);
|
|
|
|
cl = conf->add_lun(l_name);
|
|
if (cl == NULL) {
|
|
log_warnx("lun_new failed");
|
|
continue;
|
|
}
|
|
cl->set_backend(lun.backend_type.c_str());
|
|
cl->set_device_type(lun.device_type);
|
|
cl->set_blocksize(lun.blocksize);
|
|
cl->set_device_id(lun.device_id.c_str());
|
|
cl->set_serial(lun.serial_number.c_str());
|
|
cl->set_size(lun.size_blocks * lun.blocksize);
|
|
cl->set_ctl_lun(lun.lun_id);
|
|
|
|
for (const auto &pair : lun.attr_list) {
|
|
const char *key = pair.first.c_str();
|
|
const char *value = pair.second.c_str();
|
|
if (pair.first == "file" || pair.first == "dev") {
|
|
cl->set_path(value);
|
|
continue;
|
|
}
|
|
if (!cl->add_option(key, value))
|
|
log_warnx("unable to add CTL lun option "
|
|
"%s for CTL lun %ju \"%s\"",
|
|
key, (uintmax_t)lun.lun_id,
|
|
cl->name());
|
|
}
|
|
}
|
|
|
|
return (conf);
|
|
}
|
|
|
|
bool
|
|
lun::kernel_add()
|
|
{
|
|
struct ctl_lun_req req;
|
|
int error;
|
|
|
|
bzero(&req, sizeof(req));
|
|
|
|
strlcpy(req.backend, l_backend.c_str(), sizeof(req.backend));
|
|
req.reqtype = CTL_LUNREQ_CREATE;
|
|
|
|
req.reqdata.create.blocksize_bytes = l_blocksize;
|
|
|
|
if (l_size != 0)
|
|
req.reqdata.create.lun_size_bytes = l_size;
|
|
|
|
if (l_ctl_lun >= 0) {
|
|
req.reqdata.create.req_lun_id = l_ctl_lun;
|
|
req.reqdata.create.flags |= CTL_LUN_FLAG_ID_REQ;
|
|
}
|
|
|
|
req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE;
|
|
req.reqdata.create.device_type = l_device_type;
|
|
|
|
if (!l_serial.empty()) {
|
|
strncpy((char *)req.reqdata.create.serial_num, l_serial.c_str(),
|
|
sizeof(req.reqdata.create.serial_num));
|
|
req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM;
|
|
}
|
|
|
|
if (!l_device_id.empty()) {
|
|
strncpy((char *)req.reqdata.create.device_id,
|
|
l_device_id.c_str(), sizeof(req.reqdata.create.device_id));
|
|
req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID;
|
|
}
|
|
|
|
freebsd::nvlist_up nvl = options();
|
|
req.args = nvlist_pack(nvl.get(), &req.args_len);
|
|
if (req.args == NULL) {
|
|
log_warn("error packing nvlist");
|
|
return (false);
|
|
}
|
|
|
|
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
|
|
free(req.args);
|
|
|
|
if (error != 0) {
|
|
log_warn("error issuing CTL_LUN_REQ ioctl");
|
|
return (false);
|
|
}
|
|
|
|
switch (req.status) {
|
|
case CTL_LUN_ERROR:
|
|
log_warnx("LUN creation error: %s", req.error_str);
|
|
return (false);
|
|
case CTL_LUN_WARNING:
|
|
log_warnx("LUN creation warning: %s", req.error_str);
|
|
break;
|
|
case CTL_LUN_OK:
|
|
break;
|
|
default:
|
|
log_warnx("unknown LUN creation status: %d",
|
|
req.status);
|
|
return (false);
|
|
}
|
|
|
|
l_ctl_lun = req.reqdata.create.req_lun_id;
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
lun::kernel_modify() const
|
|
{
|
|
struct ctl_lun_req req;
|
|
int error;
|
|
|
|
bzero(&req, sizeof(req));
|
|
|
|
strlcpy(req.backend, l_backend.c_str(), sizeof(req.backend));
|
|
req.reqtype = CTL_LUNREQ_MODIFY;
|
|
|
|
req.reqdata.modify.lun_id = l_ctl_lun;
|
|
req.reqdata.modify.lun_size_bytes = l_size;
|
|
|
|
freebsd::nvlist_up nvl = options();
|
|
req.args = nvlist_pack(nvl.get(), &req.args_len);
|
|
if (req.args == NULL) {
|
|
log_warn("error packing nvlist");
|
|
return (false);
|
|
}
|
|
|
|
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
|
|
free(req.args);
|
|
|
|
if (error != 0) {
|
|
log_warn("error issuing CTL_LUN_REQ ioctl");
|
|
return (false);
|
|
}
|
|
|
|
switch (req.status) {
|
|
case CTL_LUN_ERROR:
|
|
log_warnx("LUN modification error: %s", req.error_str);
|
|
return (false);
|
|
case CTL_LUN_WARNING:
|
|
log_warnx("LUN modification warning: %s", req.error_str);
|
|
break;
|
|
case CTL_LUN_OK:
|
|
break;
|
|
default:
|
|
log_warnx("unknown LUN modification status: %d",
|
|
req.status);
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
lun::kernel_remove() const
|
|
{
|
|
struct ctl_lun_req req;
|
|
|
|
bzero(&req, sizeof(req));
|
|
|
|
strlcpy(req.backend, l_backend.c_str(), sizeof(req.backend));
|
|
req.reqtype = CTL_LUNREQ_RM;
|
|
|
|
req.reqdata.rm.lun_id = l_ctl_lun;
|
|
|
|
if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) {
|
|
log_warn("error issuing CTL_LUN_REQ ioctl");
|
|
return (false);
|
|
}
|
|
|
|
switch (req.status) {
|
|
case CTL_LUN_ERROR:
|
|
log_warnx("LUN removal error: %s", req.error_str);
|
|
return (false);
|
|
case CTL_LUN_WARNING:
|
|
log_warnx("LUN removal warning: %s", req.error_str);
|
|
break;
|
|
case CTL_LUN_OK:
|
|
break;
|
|
default:
|
|
log_warnx("unknown LUN removal status: %d", req.status);
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
void
|
|
kernel_handoff(struct ctld_connection *conn)
|
|
{
|
|
struct portal_group *pg = conn->conn_portal->portal_group();
|
|
struct ctl_iscsi req;
|
|
|
|
bzero(&req, sizeof(req));
|
|
|
|
req.type = CTL_ISCSI_HANDOFF;
|
|
strlcpy(req.data.handoff.initiator_name,
|
|
conn->conn_initiator_name, sizeof(req.data.handoff.initiator_name));
|
|
strlcpy(req.data.handoff.initiator_addr,
|
|
conn->conn_initiator_addr, sizeof(req.data.handoff.initiator_addr));
|
|
if (conn->conn_initiator_alias != NULL) {
|
|
strlcpy(req.data.handoff.initiator_alias,
|
|
conn->conn_initiator_alias, sizeof(req.data.handoff.initiator_alias));
|
|
}
|
|
memcpy(req.data.handoff.initiator_isid, conn->conn_initiator_isid,
|
|
sizeof(req.data.handoff.initiator_isid));
|
|
strlcpy(req.data.handoff.target_name,
|
|
conn->conn_target->name(), sizeof(req.data.handoff.target_name));
|
|
strlcpy(req.data.handoff.offload, pg->offload(),
|
|
sizeof(req.data.handoff.offload));
|
|
#ifdef ICL_KERNEL_PROXY
|
|
if (proxy_mode)
|
|
req.data.handoff.connection_id = conn->conn.conn_socket;
|
|
else
|
|
req.data.handoff.socket = conn->conn.conn_socket;
|
|
#else
|
|
req.data.handoff.socket = conn->conn.conn_socket;
|
|
#endif
|
|
req.data.handoff.portal_group_tag = pg->tag();
|
|
if (conn->conn.conn_header_digest == CONN_DIGEST_CRC32C)
|
|
req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C;
|
|
if (conn->conn.conn_data_digest == CONN_DIGEST_CRC32C)
|
|
req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C;
|
|
req.data.handoff.cmdsn = conn->conn.conn_cmdsn;
|
|
req.data.handoff.statsn = conn->conn.conn_statsn;
|
|
req.data.handoff.max_recv_data_segment_length =
|
|
conn->conn.conn_max_recv_data_segment_length;
|
|
req.data.handoff.max_send_data_segment_length =
|
|
conn->conn.conn_max_send_data_segment_length;
|
|
req.data.handoff.max_burst_length = conn->conn.conn_max_burst_length;
|
|
req.data.handoff.first_burst_length =
|
|
conn->conn.conn_first_burst_length;
|
|
req.data.handoff.immediate_data = conn->conn.conn_immediate_data;
|
|
|
|
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
|
|
log_err(1, "error issuing CTL_ISCSI ioctl; "
|
|
"dropping connection");
|
|
}
|
|
|
|
if (req.status != CTL_ISCSI_OK) {
|
|
log_errx(1, "error returned from CTL iSCSI handoff request: "
|
|
"%s; dropping connection", req.error_str);
|
|
}
|
|
}
|
|
|
|
static bool
|
|
ctl_create_port(const char *driver, const nvlist_t *nvl, uint32_t *ctl_port)
|
|
{
|
|
struct ctl_req req;
|
|
char result_buf[NVLIST_BUFSIZE];
|
|
int error;
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.reqtype = CTL_REQ_CREATE;
|
|
|
|
strlcpy(req.driver, driver, sizeof(req.driver));
|
|
req.args = nvlist_pack(nvl, &req.args_len);
|
|
if (req.args == NULL) {
|
|
log_warn("error packing nvlist");
|
|
return (false);
|
|
}
|
|
|
|
req.result = result_buf;
|
|
req.result_len = sizeof(result_buf);
|
|
error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
|
|
free(req.args);
|
|
|
|
if (error != 0) {
|
|
log_warn("error issuing CTL_PORT_REQ ioctl");
|
|
return (false);
|
|
}
|
|
if (req.status == CTL_LUN_ERROR) {
|
|
log_warnx("error returned from port creation request: %s",
|
|
req.error_str);
|
|
return (false);
|
|
}
|
|
if (req.status != CTL_LUN_OK) {
|
|
log_warnx("unknown port creation request status %d",
|
|
req.status);
|
|
return (false);
|
|
}
|
|
|
|
freebsd::nvlist_up result_nvl(nvlist_unpack(result_buf, req.result_len,
|
|
0));
|
|
if (result_nvl == NULL) {
|
|
log_warnx("error unpacking result nvlist");
|
|
return (false);
|
|
}
|
|
|
|
*ctl_port = nvlist_get_number(result_nvl.get(), "port_id");
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
portal_group_port::kernel_create_port()
|
|
{
|
|
struct portal_group *pg = p_portal_group;
|
|
struct target *targ = p_target;
|
|
|
|
freebsd::nvlist_up nvl = pg->options();
|
|
nvlist_add_string(nvl.get(), "cfiscsi_target", targ->name());
|
|
nvlist_add_string(nvl.get(), "ctld_portal_group_name", pg->name());
|
|
nvlist_add_stringf(nvl.get(), "cfiscsi_portal_group_tag", "%u",
|
|
pg->tag());
|
|
|
|
if (targ->has_alias()) {
|
|
nvlist_add_string(nvl.get(), "cfiscsi_target_alias",
|
|
targ->alias());
|
|
}
|
|
|
|
return (ctl_create_port("iscsi", nvl.get(), &p_ctl_port));
|
|
}
|
|
|
|
bool
|
|
ioctl_port::kernel_create_port()
|
|
{
|
|
freebsd::nvlist_up nvl(nvlist_create(0));
|
|
nvlist_add_stringf(nvl.get(), "pp", "%d", p_ioctl_pp);
|
|
nvlist_add_stringf(nvl.get(), "vp", "%d", p_ioctl_vp);
|
|
|
|
return (ctl_create_port("ioctl", nvl.get(), &p_ctl_port));
|
|
}
|
|
|
|
bool
|
|
kernel_port::kernel_create_port()
|
|
{
|
|
struct ctl_port_entry entry;
|
|
struct target *targ = p_target;
|
|
|
|
p_ctl_port = p_pport->ctl_port();
|
|
|
|
if (strncmp(targ->name(), "naa.", 4) == 0 &&
|
|
strlen(targ->name()) == 20) {
|
|
bzero(&entry, sizeof(entry));
|
|
entry.port_type = CTL_PORT_NONE;
|
|
entry.targ_port = p_ctl_port;
|
|
entry.flags |= CTL_PORT_WWNN_VALID;
|
|
entry.wwnn = strtoull(targ->name() + 4, NULL, 16);
|
|
if (ioctl(ctl_fd, CTL_SET_PORT_WWNS, &entry) == -1)
|
|
log_warn("CTL_SET_PORT_WWNS ioctl failed");
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
port::kernel_add()
|
|
{
|
|
struct ctl_port_entry entry;
|
|
struct ctl_lun_map lm;
|
|
struct target *targ = p_target;
|
|
int error, i;
|
|
|
|
if (!kernel_create_port())
|
|
return (false);
|
|
|
|
/* Explicitly enable mapping to block any access except allowed. */
|
|
lm.port = p_ctl_port;
|
|
lm.plun = UINT32_MAX;
|
|
lm.lun = 0;
|
|
error = ioctl(ctl_fd, CTL_LUN_MAP, &lm);
|
|
if (error != 0)
|
|
log_warn("CTL_LUN_MAP ioctl failed");
|
|
|
|
/* Map configured LUNs */
|
|
for (i = 0; i < MAX_LUNS; i++) {
|
|
if (targ->lun(i) == nullptr)
|
|
continue;
|
|
lm.port = p_ctl_port;
|
|
lm.plun = i;
|
|
lm.lun = targ->lun(i)->ctl_lun();
|
|
error = ioctl(ctl_fd, CTL_LUN_MAP, &lm);
|
|
if (error != 0)
|
|
log_warn("CTL_LUN_MAP ioctl failed");
|
|
}
|
|
|
|
/* Enable port */
|
|
bzero(&entry, sizeof(entry));
|
|
entry.targ_port = p_ctl_port;
|
|
error = ioctl(ctl_fd, CTL_ENABLE_PORT, &entry);
|
|
if (error != 0) {
|
|
log_warn("CTL_ENABLE_PORT ioctl failed");
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
port::kernel_update(const struct port *oport)
|
|
{
|
|
struct ctl_lun_map lm;
|
|
struct target *targ = p_target;
|
|
struct target *otarg = oport->p_target;
|
|
int error, i;
|
|
uint32_t olun;
|
|
|
|
p_ctl_port = oport->p_ctl_port;
|
|
|
|
/* Map configured LUNs and unmap others */
|
|
for (i = 0; i < MAX_LUNS; i++) {
|
|
lm.port = p_ctl_port;
|
|
lm.plun = i;
|
|
if (targ->lun(i) == nullptr)
|
|
lm.lun = UINT32_MAX;
|
|
else
|
|
lm.lun = targ->lun(i)->ctl_lun();
|
|
if (otarg->lun(i) == nullptr)
|
|
olun = UINT32_MAX;
|
|
else
|
|
olun = otarg->lun(i)->ctl_lun();
|
|
if (lm.lun == olun)
|
|
continue;
|
|
error = ioctl(ctl_fd, CTL_LUN_MAP, &lm);
|
|
if (error != 0)
|
|
log_warn("CTL_LUN_MAP ioctl failed");
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
ctl_remove_port(const char *driver, nvlist_t *nvl)
|
|
{
|
|
struct ctl_req req;
|
|
int error;
|
|
|
|
strlcpy(req.driver, driver, sizeof(req.driver));
|
|
req.reqtype = CTL_REQ_REMOVE;
|
|
req.args = nvlist_pack(nvl, &req.args_len);
|
|
if (req.args == NULL) {
|
|
log_warn("error packing nvlist");
|
|
return (false);
|
|
}
|
|
|
|
error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
|
|
free(req.args);
|
|
|
|
if (error != 0) {
|
|
log_warn("error issuing CTL_PORT_REQ ioctl");
|
|
return (false);
|
|
}
|
|
if (req.status == CTL_LUN_ERROR) {
|
|
log_warnx("error returned from port removal request: %s",
|
|
req.error_str);
|
|
return (false);
|
|
}
|
|
if (req.status != CTL_LUN_OK) {
|
|
log_warnx("unknown port removal request status %d", req.status);
|
|
return (false);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
portal_group_port::kernel_remove_port()
|
|
{
|
|
freebsd::nvlist_up nvl(nvlist_create(0));
|
|
nvlist_add_string(nvl.get(), "cfiscsi_target", p_target->name());
|
|
nvlist_add_stringf(nvl.get(), "cfiscsi_portal_group_tag", "%u",
|
|
p_portal_group->tag());
|
|
|
|
return (ctl_remove_port("iscsi", nvl.get()));
|
|
}
|
|
|
|
bool
|
|
ioctl_port::kernel_remove_port()
|
|
{
|
|
freebsd::nvlist_up nvl(nvlist_create(0));
|
|
nvlist_add_stringf(nvl.get(), "port_id", "%d", p_ctl_port);
|
|
|
|
return (ctl_remove_port("ioctl", nvl.get()));
|
|
}
|
|
|
|
bool
|
|
kernel_port::kernel_remove_port()
|
|
{
|
|
struct ctl_lun_map lm;
|
|
int error;
|
|
|
|
/* Disable LUN mapping. */
|
|
lm.port = p_ctl_port;
|
|
lm.plun = UINT32_MAX;
|
|
lm.lun = UINT32_MAX;
|
|
error = ioctl(ctl_fd, CTL_LUN_MAP, &lm);
|
|
if (error != 0)
|
|
log_warn("CTL_LUN_MAP ioctl failed");
|
|
return (true);
|
|
}
|
|
|
|
bool
|
|
port::kernel_remove()
|
|
{
|
|
struct ctl_port_entry entry;
|
|
int error;
|
|
|
|
/* Disable port */
|
|
bzero(&entry, sizeof(entry));
|
|
entry.targ_port = p_ctl_port;
|
|
error = ioctl(ctl_fd, CTL_DISABLE_PORT, &entry);
|
|
if (error != 0) {
|
|
log_warn("CTL_DISABLE_PORT ioctl failed");
|
|
return (false);
|
|
}
|
|
|
|
return (kernel_remove_port());
|
|
}
|
|
|
|
#ifdef ICL_KERNEL_PROXY
|
|
void
|
|
kernel_listen(struct addrinfo *ai, bool iser, int portal_id)
|
|
{
|
|
struct ctl_iscsi req;
|
|
|
|
bzero(&req, sizeof(req));
|
|
|
|
req.type = CTL_ISCSI_LISTEN;
|
|
req.data.listen.iser = iser;
|
|
req.data.listen.domain = ai->ai_family;
|
|
req.data.listen.socktype = ai->ai_socktype;
|
|
req.data.listen.protocol = ai->ai_protocol;
|
|
req.data.listen.addr = ai->ai_addr;
|
|
req.data.listen.addrlen = ai->ai_addrlen;
|
|
req.data.listen.portal_id = portal_id;
|
|
|
|
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
|
|
log_err(1, "error issuing CTL_ISCSI ioctl");
|
|
|
|
if (req.status != CTL_ISCSI_OK) {
|
|
log_errx(1, "error returned from CTL iSCSI listen: %s",
|
|
req.error_str);
|
|
}
|
|
}
|
|
|
|
void
|
|
kernel_accept(int *connection_id, int *portal_id,
|
|
struct sockaddr *client_sa, socklen_t *client_salen)
|
|
{
|
|
struct ctl_iscsi req;
|
|
struct sockaddr_storage ss;
|
|
|
|
bzero(&req, sizeof(req));
|
|
|
|
req.type = CTL_ISCSI_ACCEPT;
|
|
req.data.accept.initiator_addr = (struct sockaddr *)&ss;
|
|
|
|
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
|
|
log_err(1, "error issuing CTL_ISCSI ioctl");
|
|
|
|
if (req.status != CTL_ISCSI_OK) {
|
|
log_errx(1, "error returned from CTL iSCSI accept: %s",
|
|
req.error_str);
|
|
}
|
|
|
|
*connection_id = req.data.accept.connection_id;
|
|
*portal_id = req.data.accept.portal_id;
|
|
*client_salen = req.data.accept.initiator_addrlen;
|
|
memcpy(client_sa, &ss, *client_salen);
|
|
}
|
|
|
|
void
|
|
kernel_send(struct pdu *pdu)
|
|
{
|
|
struct ctl_iscsi req;
|
|
|
|
bzero(&req, sizeof(req));
|
|
|
|
req.type = CTL_ISCSI_SEND;
|
|
req.data.send.connection_id = pdu->pdu_connection->conn_socket;
|
|
req.data.send.bhs = pdu->pdu_bhs;
|
|
req.data.send.data_segment_len = pdu->pdu_data_len;
|
|
req.data.send.data_segment = pdu->pdu_data;
|
|
|
|
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
|
|
log_err(1, "error issuing CTL_ISCSI ioctl; "
|
|
"dropping connection");
|
|
}
|
|
|
|
if (req.status != CTL_ISCSI_OK) {
|
|
log_errx(1, "error returned from CTL iSCSI send: "
|
|
"%s; dropping connection", req.error_str);
|
|
}
|
|
}
|
|
|
|
void
|
|
kernel_receive(struct pdu *pdu)
|
|
{
|
|
struct connection *conn;
|
|
struct ctl_iscsi req;
|
|
|
|
conn = pdu->pdu_connection;
|
|
pdu->pdu_data = malloc(conn->conn_max_recv_data_segment_length);
|
|
if (pdu->pdu_data == NULL)
|
|
log_err(1, "malloc");
|
|
|
|
bzero(&req, sizeof(req));
|
|
|
|
req.type = CTL_ISCSI_RECEIVE;
|
|
req.data.receive.connection_id = conn->conn_socket;
|
|
req.data.receive.bhs = pdu->pdu_bhs;
|
|
req.data.receive.data_segment_len =
|
|
conn->conn_max_recv_data_segment_length;
|
|
req.data.receive.data_segment = pdu->pdu_data;
|
|
|
|
if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
|
|
log_err(1, "error issuing CTL_ISCSI ioctl; "
|
|
"dropping connection");
|
|
}
|
|
|
|
if (req.status != CTL_ISCSI_OK) {
|
|
log_errx(1, "error returned from CTL iSCSI receive: "
|
|
"%s; dropping connection", req.error_str);
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* ICL_KERNEL_PROXY */
|
|
|
|
/*
|
|
* XXX: I CANT INTO LATIN
|
|
*/
|
|
void
|
|
kernel_capsicate(void)
|
|
{
|
|
cap_rights_t rights;
|
|
const unsigned long cmds[] = { CTL_ISCSI };
|
|
|
|
cap_rights_init(&rights, CAP_IOCTL);
|
|
if (caph_rights_limit(ctl_fd, &rights) < 0)
|
|
log_err(1, "cap_rights_limit");
|
|
|
|
if (caph_ioctls_limit(ctl_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");
|
|
}
|
|
|