summaryrefslogtreecommitdiffstats
path: root/src/nvme
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvme')
-rw-r--r--src/nvme/api-types.h8
-rw-r--r--src/nvme/fabrics.c57
-rw-r--r--src/nvme/fabrics.h16
-rw-r--r--src/nvme/ioctl.c2
-rw-r--r--src/nvme/json.c44
-rw-r--r--src/nvme/linux.c273
-rw-r--r--src/nvme/linux.h68
-rw-r--r--src/nvme/mi.c10
-rw-r--r--src/nvme/mi.h22
-rw-r--r--src/nvme/no-json.c26
-rw-r--r--src/nvme/tree.c43
-rw-r--r--src/nvme/types.h80
-rw-r--r--src/nvme/util.c7
13 files changed, 599 insertions, 57 deletions
diff --git a/src/nvme/api-types.h b/src/nvme/api-types.h
index dac50ae..9f3604e 100644
--- a/src/nvme/api-types.h
+++ b/src/nvme/api-types.h
@@ -800,8 +800,8 @@ struct nvme_resv_report_args {
* @nsid: Namespace identifier
* @data_len: Length of @data
* @timeout: Timeout in ms
- * @mos Management Operation Specific
- * @mo Management Operation
+ * @mos: Management Operation Specific
+ * @mo: Management Operation
*/
struct nvme_io_mgmt_recv_args {
void *data;
@@ -822,8 +822,8 @@ struct nvme_io_mgmt_recv_args {
* @nsid: Namespace identifier
* @data_len: Length of @data
* @timeout: Timeout in ms
- * @mos Management Operation Specific
- * @mo Management Operation
+ * @mos: Management Operation Specific
+ * @mo: Management Operation
*/
struct nvme_io_mgmt_send_args {
void *data;
diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c
index 7134dba..3c32e27 100644
--- a/src/nvme/fabrics.c
+++ b/src/nvme/fabrics.c
@@ -110,8 +110,15 @@ static const char * const treqs[] = {
[NVMF_TREQ_NOT_SPECIFIED] = "not specified",
[NVMF_TREQ_REQUIRED] = "required",
[NVMF_TREQ_NOT_REQUIRED] = "not required",
- [NVMF_TREQ_DISABLE_SQFLOW] = "not specified, "
- "sq flow control disable supported",
+ [NVMF_TREQ_NOT_SPECIFIED |
+ NVMF_TREQ_DISABLE_SQFLOW] = "not specified, "
+ "sq flow control disable supported",
+ [NVMF_TREQ_REQUIRED |
+ NVMF_TREQ_DISABLE_SQFLOW] = "required, "
+ "sq flow control disable supported",
+ [NVMF_TREQ_NOT_REQUIRED |
+ NVMF_TREQ_DISABLE_SQFLOW] = "not required, "
+ "sq flow control disable supported",
};
const char *nvmf_treq_str(__u8 treq)
@@ -216,6 +223,8 @@ static struct nvme_fabrics_config *merge_config(nvme_ctrl_t c,
NVMF_DEF_CTRL_LOSS_TMO);
MERGE_CFG_OPTION(ctrl_cfg, cfg, fast_io_fail_tmo, 0);
MERGE_CFG_OPTION(ctrl_cfg, cfg, tos, -1);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, keyring, 0);
+ MERGE_CFG_OPTION(ctrl_cfg, cfg, tls_key, 0);
MERGE_CFG_OPTION(ctrl_cfg, cfg, duplicate_connect, false);
MERGE_CFG_OPTION(ctrl_cfg, cfg, disable_sqflow, false);
MERGE_CFG_OPTION(ctrl_cfg, cfg, hdr_digest, false);
@@ -243,6 +252,8 @@ void nvmf_update_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg)
NVMF_DEF_CTRL_LOSS_TMO);
UPDATE_CFG_OPTION(ctrl_cfg, cfg, fast_io_fail_tmo, 0);
UPDATE_CFG_OPTION(ctrl_cfg, cfg, tos, -1);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, keyring, 0);
+ UPDATE_CFG_OPTION(ctrl_cfg, cfg, tls_key, 0);
UPDATE_CFG_OPTION(ctrl_cfg, cfg, duplicate_connect, false);
UPDATE_CFG_OPTION(ctrl_cfg, cfg, disable_sqflow, false);
UPDATE_CFG_OPTION(ctrl_cfg, cfg, hdr_digest, false);
@@ -517,6 +528,9 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr)
cfg->fast_io_fail_tmo, false)) ||
(strcmp(transport, "loop") &&
add_int_argument(argstr, "tos", cfg->tos, true)) ||
+ add_int_argument(argstr, "keyring", cfg->keyring, false) ||
+ (!strcmp(transport, "tcp") &&
+ add_int_argument(argstr, "tls_key", cfg->tls_key, false)) ||
add_bool_argument(argstr, "duplicate_connect",
cfg->duplicate_connect) ||
add_bool_argument(argstr, "disable_sqflow",
@@ -894,20 +908,20 @@ static void sanitize_discovery_log_entry(struct nvmf_disc_log_entry *e)
switch (e->adrfam) {
case NVMF_ADDR_FAMILY_IP4:
case NVMF_ADDR_FAMILY_IP6:
- strchomp(e->traddr, NVMF_TRADDR_SIZE - 1);
- strchomp(e->trsvcid, NVMF_TRSVCID_SIZE - 1);
+ strchomp(e->traddr, NVMF_TRADDR_SIZE);
+ strchomp(e->trsvcid, NVMF_TRSVCID_SIZE);
break;
}
break;
case NVMF_TRTYPE_FC:
switch (e->adrfam) {
case NVMF_ADDR_FAMILY_FC:
- strchomp(e->traddr, NVMF_TRADDR_SIZE - 1);
+ strchomp(e->traddr, NVMF_TRADDR_SIZE);
break;
}
break;
case NVMF_TRTYPE_LOOP:
- strchomp(e->traddr, NVMF_TRADDR_SIZE - 1);
+ strchomp(e->traddr, NVMF_TRADDR_SIZE);
break;
}
}
@@ -998,6 +1012,31 @@ static int uuid_from_device_tree(char *system_uuid)
#define PATH_DMI_ENTRIES "/sys/firmware/dmi/entries"
+/*
+ * See System Management BIOS (SMBIOS) Reference Specification
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf
+ */
+#define DMI_SYSTEM_INFORMATION 1
+
+static bool is_dmi_uuid_valid(const char *buf, size_t len)
+{
+ int i;
+
+ /* UUID bytes are from byte 8 to 23 */
+ if (len < 24)
+ return false;
+
+ /* Test it's a invalid UUID with all zeros */
+ for (i = 8; i < 24; i++) {
+ if (buf[i])
+ break;
+ }
+ if (i == 24)
+ return false;
+
+ return true;
+}
+
static int uuid_from_dmi_entries(char *system_uuid)
{
int f;
@@ -1025,7 +1064,7 @@ static int uuid_from_dmi_entries(char *system_uuid)
continue;
if (sscanf(buf, "%d", &type) != 1)
continue;
- if (type != 1)
+ if (type != DMI_SYSTEM_INFORMATION)
continue;
sprintf(filename, "%s/%s/raw", PATH_DMI_ENTRIES, de->d_name);
f = open(filename, O_RDONLY);
@@ -1033,8 +1072,10 @@ static int uuid_from_dmi_entries(char *system_uuid)
continue;
len = read(f, buf, 512);
close(f);
- if (len <= 0)
+
+ if (!is_dmi_uuid_valid(buf, len))
continue;
+
/* Sigh. https://en.wikipedia.org/wiki/Overengineering */
/* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */
sprintf(system_uuid,
diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h
index 272bb40..3664a85 100644
--- a/src/nvme/fabrics.h
+++ b/src/nvme/fabrics.h
@@ -35,6 +35,8 @@
* @nr_write_queues: Number of queues to use for exclusively for writing
* @nr_poll_queues: Number of queues to reserve for polling completions
* @tos: Type of service
+ * @keyring: Keyring to store and lookup keys
+ * @tls_key: TLS PSK for the connection
* @duplicate_connect: Allow multiple connections to the same target
* @disable_sqflow: Disable controller sq flow control
* @hdr_digest: Generate/verify header digest (TCP)
@@ -53,6 +55,8 @@ struct nvme_fabrics_config {
int nr_write_queues;
int nr_poll_queues;
int tos;
+ int keyring;
+ int tls_key;
bool duplicate_connect;
bool disable_sqflow;
@@ -253,7 +257,11 @@ char *nvmf_hostnqn_generate();
/**
* nvmf_hostnqn_from_file() - Reads the host nvm qualified name from the config
- * default location in @SYSCONFDIR@/nvme/
+ * default location
+ *
+ * Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme.
+ * $SYSCONFDIR is usually /etc.
+ *
* Return: The host nqn, or NULL if unsuccessful. If found, the caller
* is responsible to free the string.
*/
@@ -261,7 +269,11 @@ char *nvmf_hostnqn_from_file();
/**
* nvmf_hostid_from_file() - Reads the host identifier from the config default
- * location in @SYSCONFDIR@/nvme/.
+ * location
+ *
+ * Retrieve the host idenditifer from the config file located in $SYSCONFDIR/nvme/.
+ * $SYSCONFDIR is usually /etc.
+ *
* Return: The host identifier, or NULL if unsuccessful. If found, the caller
* is responsible to free the string.
*/
diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c
index b2d92ef..2b5e09d 100644
--- a/src/nvme/ioctl.c
+++ b/src/nvme/ioctl.c
@@ -438,6 +438,8 @@ int nvme_get_log_page(int fd, __u32 xfer_len, struct nvme_get_log_args *args)
void *ptr = args->log;
int ret;
+ args->fd = fd;
+
/*
* 4k is the smallest possible transfer unit, so restricting to 4k
* avoids having to check the MDTS value of the controller.
diff --git a/src/nvme/json.c b/src/nvme/json.c
index 072b622..a74b5a4 100644
--- a/src/nvme/json.c
+++ b/src/nvme/json.c
@@ -17,6 +17,7 @@
#include "fabrics.h"
#include "log.h"
#include "private.h"
+#include "linux.h"
#define JSON_UPDATE_INT_OPTION(c, k, a, o) \
if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_int(o);
@@ -64,6 +65,27 @@ static void json_update_attributes(nvme_ctrl_t c,
if (!strcmp("discovery", key_str) &&
!nvme_ctrl_is_discovery_ctrl(c))
nvme_ctrl_set_discovery_ctrl(c, true);
+ /*
+ * The JSON configuration holds the keyring description
+ * which needs to be converted into the keyring serial number.
+ */
+ if (!strcmp("keyring", key_str) && cfg->keyring == 0) {
+ long keyring;
+
+ keyring = nvme_lookup_keyring(json_object_get_string(val_obj));
+ if (keyring) {
+ cfg->keyring = keyring;
+ nvme_set_keyring(cfg->keyring);
+ }
+ }
+ if (!strcmp("tls_key", key_str) && cfg->tls_key == 0) {
+ long key;
+
+ key = nvme_lookup_key("psk",
+ json_object_get_string(val_obj));
+ if (key)
+ cfg->tls_key = key;
+ }
}
}
@@ -299,6 +321,28 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c)
if (nvme_ctrl_is_discovery_ctrl(c))
json_object_object_add(port_obj, "discovery",
json_object_new_boolean(true));
+ /*
+ * Store the keyring description in the JSON config file.
+ */
+ if (cfg->keyring) {
+ char *desc = nvme_describe_key_serial(cfg->keyring);
+
+ if (desc) {
+ json_object_object_add(port_obj, "keyring",
+ json_object_new_string(desc));
+ free(desc);
+ }
+ }
+ if (cfg->tls_key) {
+ char *desc = nvme_describe_key_serial(cfg->tls_key);
+
+ if (desc) {
+ json_object_object_add(port_obj, "tls_key",
+ json_object_new_string(desc));
+ free(desc);
+ }
+ }
+
json_object_array_add(ctrl_array, port_obj);
}
diff --git a/src/nvme/linux.c b/src/nvme/linux.c
index cae4036..c6eedc2 100644
--- a/src/nvme/linux.c
+++ b/src/nvme/linux.c
@@ -21,6 +21,7 @@
#include <openssl/engine.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
+#include <openssl/kdf.h>
#ifdef CONFIG_OPENSSL_3
#include <openssl/core_names.h>
@@ -28,6 +29,10 @@
#endif
#endif
+#ifdef CONFIG_KEYUTILS
+#include <keyutils.h>
+#endif
+
#include <ccan/endian/endian.h>
#include "linux.h"
@@ -470,7 +475,143 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
memcpy(key, secret, key_len);
return 0;
}
-#endif /* !CONFIG_OPENSSL */
+
+static int derive_nvme_keys(const char *hostnqn, const char *identity,
+ int hmac, unsigned char *configured,
+ unsigned char *psk, int key_len)
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+#else /* CONFIG_OPENSSL */
+static int derive_retained_key(const EVP_MD *md, const char *hostnqn,
+ unsigned char *generated,
+ unsigned char *retained,
+ size_t key_len)
+{
+ EVP_PKEY_CTX *ctx;
+ int ret;
+
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(ctx) <= 0) {
+ ret = -ENOMEM;
+ goto out_free_ctx;
+ }
+ ret = -ENOKEY;
+ if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0)
+ goto out_free_ctx;
+ if (EVP_PKEY_CTX_set1_hkdf_key(ctx, generated, key_len) <= 0)
+ goto out_free_ctx;
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)"tls13 ", 6) <= 0)
+ goto out_free_ctx;
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)"HostNQN", 7) <= 0)
+ goto out_free_ctx;
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)hostnqn, strlen(hostnqn)) <= 0)
+ goto out_free_ctx;
+
+ if (EVP_PKEY_derive(ctx, retained, &key_len) > 0)
+ ret = key_len;
+
+out_free_ctx:
+ if (ret < 0) {
+ errno = -ret;
+ ret = -1;
+ }
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+static int derive_tls_key(const EVP_MD *md, const char *identity,
+ unsigned char *retained,
+ unsigned char *psk, size_t key_len)
+{
+ EVP_PKEY_CTX *ctx;
+ int ret;
+
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (!ctx) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(ctx) <= 0) {
+ ret = -ENOMEM;
+ goto out_free_ctx;
+ }
+ ret = -ENOKEY;
+ if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0)
+ goto out_free_ctx;
+ if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0)
+ goto out_free_ctx;
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)"tls13 ", 6) <= 0)
+ goto out_free_ctx;
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)"nvme-tls-psk", 12) <= 0)
+ goto out_free_ctx;
+ if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
+ (const unsigned char *)identity,
+ strlen(identity)) <= 0)
+ goto out_free_ctx;
+
+ if (EVP_PKEY_derive(ctx, psk, &key_len) > 0)
+ ret = key_len;
+
+out_free_ctx:
+ EVP_PKEY_CTX_free(ctx);
+ if (ret < 0) {
+ errno = -ret;
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int derive_nvme_keys(const char *hostnqn, const char *identity,
+ int hmac, unsigned char *configured,
+ unsigned char *psk, int key_len)
+{
+ const EVP_MD *md;
+ unsigned char *retained;
+ int ret = -1;
+
+ if (!hostnqn || !identity) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (hmac) {
+ case 1:
+ md = EVP_sha256();
+ break;
+ case 2:
+ md = EVP_sha384();
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ retained = malloc(key_len);
+ if (!retained) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ret = derive_retained_key(md, hostnqn, configured, retained, key_len);
+ if (ret > 0)
+ ret = derive_tls_key(md, identity, retained, psk, key_len);
+ free(retained);
+ return ret;
+}
+#endif /* CONFIG_OPENSSL */
#ifdef CONFIG_OPENSSL_1
int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
@@ -638,3 +779,133 @@ out:
return err;
}
#endif /* !CONFIG_OPENSSL_3 */
+
+#ifdef CONFIG_KEYUTILS
+long nvme_lookup_keyring(const char *keyring)
+{
+ key_serial_t keyring_id;
+
+ keyring_id = find_key_by_type_and_desc("keyring", keyring, 0);
+ if (keyring_id < 0)
+ return 0;
+ return keyring_id;
+}
+
+char *nvme_describe_key_serial(long key_id)
+{
+ char *desc;
+
+ if (keyctl_describe_alloc(key_id, &desc) < 0)
+ desc = NULL;
+ return desc;
+}
+
+long nvme_lookup_key(const char *type, const char *identity)
+{
+ key_serial_t key;
+
+ key = keyctl_search(KEY_SPEC_SESSION_KEYRING, type, identity, 0);
+ if (key < 0)
+ return 0;
+ return key;
+}
+
+int nvme_set_keyring(long key_id)
+{
+ long err;
+
+ err = keyctl_link(key_id, KEY_SPEC_SESSION_KEYRING);
+ if (err < 0)
+ return -1;
+ return 0;
+}
+
+long nvme_insert_tls_key(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn, int hmac,
+ unsigned char *configured_key, int key_len)
+{
+ key_serial_t keyring_id, key = 0;
+ char *identity;
+ unsigned char *psk;
+ int ret = -1;
+
+ keyring_id = nvme_lookup_keyring(keyring);
+ if (keyring_id == 0)
+ return -1;
+
+ identity = malloc(strlen(hostnqn) + strlen(subsysnqn) + 12);
+ if (!identity) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ sprintf(identity, "NVMe0R%02d %s %s", hmac, hostnqn, subsysnqn);
+
+ psk = malloc(key_len);
+ if (!psk) {
+ errno = ENOMEM;
+ goto out_free_identity;
+ }
+ memset(psk, 0, key_len);
+ ret = derive_nvme_keys(hostnqn, identity, hmac,
+ configured_key, psk, key_len);
+ if (ret != key_len)
+ goto out_free_psk;
+
+ key = keyctl_search(keyring_id, key_type, identity, 0);
+ if (key > 0) {
+ if (keyctl_update(key, psk, key_len) < 0)
+ key = 0;
+ } else {
+ key = add_key(key_type, identity,
+ psk, key_len, keyring_id);
+ if (key < 0)
+ key = 0;
+ }
+out_free_psk:
+ free(psk);
+out_free_identity:
+ free(identity);
+ return key;
+}
+
+#else
+long nvme_lookup_keyring(const char *keyring)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "\
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return 0;
+}
+
+char *nvme_describe_key_serial(long key_id)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "\
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return NULL;
+}
+
+long nvme_lookup_key(const char *type, const char *identity)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "\
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return 0;
+}
+
+int nvme_set_keyring(long key_id)
+{
+ nvme_msg(NULL, LOG_ERR, "key operations not supported; "\
+ "recompile with keyutils support.\n");
+ errno = ENOTSUP;
+ return -1;
+}
+
+long nvme_insert_tls_key(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn, int hmac,
+ unsigned char *configured_key, int key_len)
+{
+ return derive_nvme_keys(NULL, NULL, 0, NULL, NULL, 0);
+}
+#endif
diff --git a/src/nvme/linux.h b/src/nvme/linux.h
index aa4c91a..37ba9d4 100644
--- a/src/nvme/linux.h
+++ b/src/nvme/linux.h
@@ -194,4 +194,72 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
unsigned int key_len, unsigned char *secret,
unsigned char *key);
+/**
+ * nvme_lookup_keyring() - Lookup keyring serial number
+ * @keyring: Keyring name
+ *
+ * Looks up the serial number of the keyring @keyring.
+ *
+ * Return: The key serial number of the keyring
+ * or 0 with errno set otherwise.
+ */
+long nvme_lookup_keyring(const char *keyring);
+
+/**
+ * nvme_describe_key_serial() - Return key description
+ * @key_id: Key serial number
+ *
+ * Fetches the description of the key or keyring identified
+ * by the serial number @key_id.
+ *
+ * Return: The description of @key_id or NULL on failure.
+ * The returned string needs to be freed by the caller.
+ */
+char *nvme_describe_key_serial(long key_id);
+
+/**
+ * nvme_lookup_key() - Lookup key serial number
+ * @type: Key type
+ * @identity: Key description
+ *
+ * Looks up the serial number of the key @identity
+ * with type %type in the current session keyring.
+ *
+ * Return: The key serial number of the key
+ * or 0 with errno set otherwise.
+ */
+long nvme_lookup_key(const char *type, const char *identity);
+
+/**
+ * nvme_set_keyring() - Link keyring for lookup
+ * @keyring_id: Keyring id
+ *
+ * Links @keyring_id into the session keyring such that
+ * its keys are available for further key lookups.
+ *
+ * Return: 0 on success, a negative number on error
+ * with errno set.
+ */
+int nvme_set_keyring(long keyring_id);
+
+/**
+ * nvme_insert_tls_key() - Derive and insert TLS key
+ * @keyring: Keyring to use
+ * @key_type: Type of the resulting key
+ * @hostnqn: Host NVMe Qualified Name
+ * @subsysnqn: Subsystem NVMe Qualified Name
+ * @hmac: HMAC algorithm
+ * @configured_key: Configured key data to derive the key from
+ * @key_len: Length of @configured_key
+ *
+ * Derives a 'retained' TLS key as specified in NVMe TCP 1.0a and
+ * stores it as type @key_type in the keyring specified by @keyring.
+ *
+ * Return: The key serial number if the key could be inserted into
+ * the keyring or 0 with errno otherwise.
+ */
+long nvme_insert_tls_key(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn, int hmac,
+ unsigned char *configured_key, int key_len);
+
#endif /* _LIBNVME_LINUX_H */
diff --git a/src/nvme/mi.c b/src/nvme/mi.c
index adf1753..391ba1a 100644
--- a/src/nvme/mi.c
+++ b/src/nvme/mi.c
@@ -841,9 +841,10 @@ static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl,
return rc;
}
-int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args)
+int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl, __u32 xfer_size,
+ struct nvme_get_log_args *args)
{
- const size_t max_xfer_size = 4096;
+ const size_t max_xfer_size = xfer_size;
off_t xfer_offset;
int rc = 0;
@@ -887,6 +888,11 @@ int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args)
return rc;
}
+int nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl, struct nvme_get_log_args *args)
+{
+ return nvme_mi_admin_get_log_page(ctrl, 4096, args);
+}
+
int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl,
struct nvme_security_send_args *args)
{
diff --git a/src/nvme/mi.h b/src/nvme/mi.h
index 5659159..12dbf6f 100644
--- a/src/nvme/mi.h
+++ b/src/nvme/mi.h
@@ -1418,6 +1418,28 @@ static inline int nvme_mi_admin_identify_secondary_ctrl_list(nvme_mi_ctrl_t ctrl
}
/**
+ * nvme_mi_admin_get_log_page() - Retrieve log page data from controller
+ * @ctrl: Controller to query
+ * @xfer_len: The chunk size of the read
+ * @args: Get Log Page command arguments
+ *
+ * Performs a Get Log Page Admin command as specified by @args. Response data
+ * is stored in @args->data, which should be a buffer of @args->data_len bytes.
+ * Resulting data length is stored in @args->data_len on successful
+ * command completion.
+ *
+ * This request may be implemented as multiple log page commands, in order
+ * to fit within MI message-size limits.
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ *
+ * See: &struct nvme_get_log_args
+ */
+int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl, __u32 xfer_len,
+ struct nvme_get_log_args *args);
+
+/**
* nvme_mi_admin_get_log() - Retrieve log page data from controller
* @ctrl: Controller to query
* @args: Get Log Page command arguments
diff --git a/src/nvme/no-json.c b/src/nvme/no-json.c
new file mode 100644
index 0000000..171144e
--- /dev/null
+++ b/src/nvme/no-json.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2023 SUSE Software Solutions
+ *
+ * Authors: Daniel Wagner <dwagner@suse.de>
+ */
+
+#include "tree.h"
+
+#include <errno.h>
+
+int json_read_config(nvme_root_t r, const char *config_file)
+{
+ return -ENOTSUP;
+}
+
+int json_update_config(nvme_root_t r, const char *config_file)
+{
+ return -ENOTSUP;
+}
+
+int json_dump_tree(nvme_root_t r)
+{
+ return -ENOTSUP;
+}
diff --git a/src/nvme/tree.c b/src/nvme/tree.c
index 18826bf..3484463 100644
--- a/src/nvme/tree.c
+++ b/src/nvme/tree.c
@@ -363,6 +363,7 @@ static void __nvme_free_ns(struct nvme_ns *n)
/* Stub for SWIG */
void nvme_free_ns(struct nvme_ns *n)
{
+ __nvme_free_ns(n);
}
static void __nvme_free_subsystem(struct nvme_subsystem *s)
@@ -459,6 +460,7 @@ static void __nvme_free_host(struct nvme_host *h)
/* Stub for SWIG */
void nvme_free_host(struct nvme_host *h)
{
+ __nvme_free_host(h);
}
struct nvme_host *nvme_lookup_host(nvme_root_t r, const char *hostnqn,
@@ -1862,27 +1864,62 @@ free_ns:
return NULL;
}
+static inline bool nvme_ns_is_generic(const char *name)
+{
+ int instance, head_instance;
+
+ if (sscanf(name, "ng%dn%d", &instance, &head_instance) != 2)
+ return false;
+ return true;
+}
+
+static char *nvme_ns_generic_to_blkdev(const char *generic)
+{
+
+ int instance, head_instance;
+ char blkdev[PATH_MAX];
+
+ if (!nvme_ns_is_generic(generic))
+ return strdup(generic);
+
+ sscanf(generic, "ng%dn%d", &instance, &head_instance);
+ sprintf(blkdev, "nvme%dn%d", instance, head_instance);
+
+ return strdup(blkdev);
+}
+
static struct nvme_ns *__nvme_scan_namespace(const char *sysfs_dir, const char *name)
{
struct nvme_ns *n;
char *path;
int ret;
+ char *blkdev;
- ret = asprintf(&path, "%s/%s", sysfs_dir, name);
- if (ret < 0) {
+ blkdev = nvme_ns_generic_to_blkdev(name);
+ if (!blkdev) {
errno = ENOMEM;
return NULL;
}
- n = nvme_ns_open(name);
+ ret = asprintf(&path, "%s/%s", sysfs_dir, blkdev);
+ if (ret < 0) {
+ errno = ENOMEM;
+ goto free_blkdev;
+ }
+
+ n = nvme_ns_open(blkdev);
if (!n)
goto free_path;
n->sysfs_dir = path;
+
+ free(blkdev);
return n;
free_path:
free(path);
+free_blkdev:
+ free(blkdev);
return NULL;
}
diff --git a/src/nvme/types.h b/src/nvme/types.h
index 929d658..eed50ef 100644
--- a/src/nvme/types.h
+++ b/src/nvme/types.h
@@ -2239,8 +2239,8 @@ struct nvme_id_ctrl_nvm {
__u8 wzsl;
__u8 wusl;
__u8 dmrl;
- __u32 dmrsl;
- __u64 dmsl;
+ __le32 dmrsl;
+ __le64 dmsl;
__u8 rsvd16[4080];
};
@@ -2407,7 +2407,7 @@ struct nvme_secondary_ctrl_list {
* @iocsc: List of supported IO Command Set Combination vectors
*/
struct nvme_id_iocs {
- __u64 iocsc[512];
+ __le64 iocsc[512];
};
/**
@@ -2515,12 +2515,18 @@ struct nvme_supported_log_pages {
* related, this field shall be cleared to %0h. If the error
* is transport related, this field shall be set to the type
* of the transport - see &enum nvme_trtype.
- * @rsvd: Reserved
+ * @csi: Command Set Indicator: This field contains command set
+ * indicator for the command that the error is associated
+ * with.
+ * @opcode: Opcode: This field contains opcode for the command that
+ * the error is associated with.
* @cs: Command Specific Information: This field contains command
* specific information. If used, the command definition
* specifies the information returned.
* @trtype_spec_info: Transport Type Specific Information
- * @rsvd2: Reserved
+ * @rsvd: Reserved: [62:42]
+ * @log_page_version: This field shall be set to 1h. If set, @csi and @opcode
+ * will have valid values.
*/
struct nvme_error_log_page {
__le64 error_count;
@@ -2532,10 +2538,12 @@ struct nvme_error_log_page {
__le32 nsid;
__u8 vs;
__u8 trtype;
- __u8 rsvd[2];
+ __u8 csi;
+ __u8 opcode;
__le64 cs;
__le16 trtype_spec_info;
- __u8 rsvd2[22];
+ __u8 rsvd[21];
+ __u8 log_page_version;
};
enum nvme_err_pel {
@@ -4147,15 +4155,15 @@ enum nvme_fdp_config_fdpa {
* @ruhs: Reclaim Unit Handle descriptors (&struct nvme_fdp_ruh_desc)
*/
struct nvme_fdp_config_desc {
- __u16 size;
+ __le16 size;
__u8 fdpa;
__u8 vss;
- __u32 nrg;
- __u16 nruh;
- __u16 maxpids;
- __u32 nnss;
- __u64 runs;
- __u32 erutl;
+ __le32 nrg;
+ __le16 nruh;
+ __le16 maxpids;
+ __le32 nnss;
+ __le64 runs;
+ __le32 erutl;
__u8 rsvd28[36];
struct nvme_fdp_ruh_desc ruhs[];
};
@@ -4170,10 +4178,10 @@ struct nvme_fdp_config_desc {
* @configs: FDP Configuration descriptors (&struct nvme_fdp_config_desc)
*/
struct nvme_fdp_config_log {
- __u16 n;
+ __le16 n;
__u8 version;
__u8 rsvd3;
- __u32 size;
+ __le32 size;
__u8 rsvd8[8];
struct nvme_fdp_config_desc configs[];
};
@@ -4209,7 +4217,7 @@ struct nvme_fdp_ruhu_desc {
* @ruhus: Reclaim Unit Handle Usage descriptors
*/
struct nvme_fdp_ruhu_log {
- __u16 nruh;
+ __le16 nruh;
__u8 rsvd2[6];
struct nvme_fdp_ruhu_desc ruhus[];
};
@@ -4268,8 +4276,8 @@ enum nvme_fdp_event_realloc_flags {
struct nvme_fdp_event_realloc {
__u8 flags;
__u8 rsvd1;
- __u16 nlbam;
- __u64 lba;
+ __le16 nlbam;
+ __le64 lba;
__u8 rsvd12[4];
};
@@ -4301,11 +4309,11 @@ enum nvme_fdp_event_flags {
struct nvme_fdp_event {
__u8 type;
__u8 flags;
- __u16 pid;
+ __le16 pid;
struct nvme_timestamp ts;
- __u32 nsid;
+ __le32 nsid;
__u8 type_specific[16];
- __u16 rgid;
+ __le16 rgid;
__u8 ruhid;
__u8 rsvd35[5];
__u8 vs[24];
@@ -4318,7 +4326,7 @@ struct nvme_fdp_event {
* @events: FDP Events (&struct nvme_fdp_event)
*/
struct nvme_fdp_events_log {
- __u32 n;
+ __le32 n;
__u8 rsvd4[60];
struct nvme_fdp_event events[63];
};
@@ -4330,7 +4338,7 @@ struct nvme_fdp_events_log {
* @rsvd24: Reserved
*/
struct nvme_feat_fdp_events_cdw11 {
- __u16 phndl;
+ __le16 phndl;
__u8 noet;
__u8 rsvd24;
};
@@ -4364,10 +4372,10 @@ struct nvme_fdp_supported_event_desc {
* @rsvd16: Reserved
*/
struct nvme_fdp_ruh_status_desc {
- __u16 pid;
- __u16 ruhid;
- __u32 earutr;
- __u64 ruamw;
+ __le16 pid;
+ __le16 ruhid;
+ __le32 earutr;
+ __le64 ruamw;
__u8 rsvd16[16];
};
@@ -4379,7 +4387,7 @@ struct nvme_fdp_ruh_status_desc {
*/
struct nvme_fdp_ruh_status {
__u8 rsvd0[14];
- __u16 nruhsd;
+ __le16 nruhsd;
struct nvme_fdp_ruh_status_desc ruhss[];
};
@@ -4445,7 +4453,7 @@ enum nvme_apst_entry {
struct nvme_metadata_element_desc {
__u8 type;
__u8 rev;
- __u16 len;
+ __le16 len;
__u8 val[0];
};
@@ -4541,8 +4549,8 @@ struct nvme_lba_range_type_entry {
__u8 type;
__u8 attributes;
__u8 rsvd2[14];
- __u64 slba;
- __u64 nlb;
+ __le64 slba;
+ __le64 nlb;
__u8 guid[16];
__u8 rsvd48[16];
};
@@ -4640,8 +4648,8 @@ struct nvme_copy_range {
__le16 nlb;
__u8 rsvd18[6];
__le32 eilbrt;
- __le16 elbatm;
__le16 elbat;
+ __le16 elbatm;
};
/**
@@ -4661,8 +4669,8 @@ struct nvme_copy_range_f1 {
__le16 nlb;
__u8 rsvd18[8];
__u8 elbt[10];
- __le16 elbatm;
__le16 elbat;
+ __le16 elbatm;
};
/**
@@ -4779,10 +4787,12 @@ struct nvme_id_directives {
* enum nvme_directive_types - Directives Supported or Enabled
* @NVME_ID_DIR_ID_BIT: Identify directive is supported
* @NVME_ID_DIR_SD_BIT: Streams directive is supported
+ * @NVME_ID_DIR_DP_BIT: Direct Placement directive is supported
*/
enum nvme_directive_types {
NVME_ID_DIR_ID_BIT = 0,
NVME_ID_DIR_SD_BIT = 1,
+ NVME_ID_DIR_DP_BIT = 2,
};
/**
@@ -4973,7 +4983,7 @@ union nvmf_tsas {
__u8 prtype;
__u8 cms;
__u8 rsvd3[5];
- __u16 pkey;
+ __le16 pkey;
__u8 rsvd10[246];
} rdma;
struct tcp {
diff --git a/src/nvme/util.c b/src/nvme/util.c
index 0354afe..e7cbc8a 100644
--- a/src/nvme/util.c
+++ b/src/nvme/util.c
@@ -403,14 +403,17 @@ void nvme_init_copy_range_f1(struct nvme_copy_range_f1 *copy, __u16 *nlbs,
__u64 *slbas, __u64 *eilbrts, __u32 *elbatms,
__u32 *elbats, __u16 nr)
{
- int i;
+ int i, j;
for (i = 0; i < nr; i++) {
copy[i].nlb = cpu_to_le16(nlbs[i]);
copy[i].slba = cpu_to_le64(slbas[i]);
- copy[i].elbt[2] = cpu_to_le64(eilbrts[i]);
copy[i].elbatm = cpu_to_le16(elbatms[i]);
copy[i].elbat = cpu_to_le16(elbats[i]);
+ for (j = 0; j < 8; j++)
+ copy[i].elbt[9 - j] = (eilbrts[i] >> (8 * j)) & 0xff;
+ copy[i].elbt[1] = 0;
+ copy[i].elbt[0] = 0;
}
}