summaryrefslogtreecommitdiffstats
path: root/src/nvme
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-11-09 08:04:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-11-09 08:04:14 +0000
commit90e4bb6ac55713bbad28f98f515b9d5dcafb7c82 (patch)
tree8d020c088a40dc6f1db2a16e6697db7a22238da0 /src/nvme
parentReleasing debian version 1.10-1. (diff)
downloadlibnvme-90e4bb6ac55713bbad28f98f515b9d5dcafb7c82.tar.xz
libnvme-90e4bb6ac55713bbad28f98f515b9d5dcafb7c82.zip
Merging upstream version 1.11.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/nvme')
-rw-r--r--src/nvme/api-types.h2
-rw-r--r--src/nvme/fabrics.c95
-rw-r--r--src/nvme/fabrics.h13
-rw-r--r--src/nvme/ioctl.c104
-rw-r--r--src/nvme/ioctl.h36
-rw-r--r--src/nvme/json.c155
-rw-r--r--src/nvme/linux.c452
-rw-r--r--src/nvme/linux.h37
-rw-r--r--src/nvme/mi.c82
-rw-r--r--src/nvme/mi.h94
-rw-r--r--src/nvme/private.h6
-rw-r--r--src/nvme/tree.c138
-rw-r--r--src/nvme/tree.h45
-rw-r--r--src/nvme/types.h444
-rw-r--r--src/nvme/util.c6
-rw-r--r--src/nvme/util.h2
16 files changed, 1386 insertions, 325 deletions
diff --git a/src/nvme/api-types.h b/src/nvme/api-types.h
index 296a7b0..4cf8e8c 100644
--- a/src/nvme/api-types.h
+++ b/src/nvme/api-types.h
@@ -511,6 +511,7 @@ struct nvme_get_property_args {
* @owpass: Overwrite pass count
* @oipbp: Set to overwrite invert pattern between passes
* @nodas: Set to not deallocate blocks after sanitizing
+ * @emvs: Set to enter media verification state
*/
struct nvme_sanitize_nvm_args {
__u32 *result;
@@ -523,6 +524,7 @@ struct nvme_sanitize_nvm_args {
__u8 owpass;
bool oipbp;
bool nodas;
+ bool emvs;
};
/**
diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c
index acf12bc..69acf04 100644
--- a/src/nvme/fabrics.c
+++ b/src/nvme/fabrics.c
@@ -309,6 +309,22 @@ static int __add_bool_argument(char **argstr, char *tok, bool arg)
return 0;
}
+static int __add_hex_argument(char **argstr, char *tok, int arg, bool allow_zero)
+{
+ char *nstr;
+
+ if (arg < 0 || (!arg && !allow_zero))
+ return 0;
+ if (asprintf(&nstr, "%s,%s=0x%08x", *argstr, tok, arg) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ free(*argstr);
+ *argstr = nstr;
+
+ return 0;
+}
+
static int __add_int_argument(char **argstr, char *tok, int arg, bool allow_zero)
{
char *nstr;
@@ -363,7 +379,7 @@ static int __nvmf_supported_options(nvme_root_t r);
!__nvmf_supported_options(r) && (r)->options->tok; \
})
-#define add_bool_argument(o, argstr, tok, arg) \
+#define add_bool_argument(r, argstr, tok, arg) \
({ \
int ret; \
if (nvmf_check_option(r, tok)) { \
@@ -379,7 +395,24 @@ static int __nvmf_supported_options(nvme_root_t r);
ret; \
})
-#define add_int_argument(o, argstr, tok, arg, allow_zero) \
+#define add_hex_argument(r, argstr, tok, arg, allow_zero) \
+({ \
+ int ret; \
+ if (nvmf_check_option(r, tok)) { \
+ ret = __add_hex_argument(argstr, \
+ stringify(tok), \
+ arg, \
+ allow_zero); \
+ } else { \
+ nvme_msg(r, LOG_DEBUG, \
+ "option \"%s\" ignored\n", \
+ stringify(tok)); \
+ ret = 0; \
+ } \
+ ret; \
+})
+
+#define add_int_argument(r, argstr, tok, arg, allow_zero) \
({ \
int ret; \
if (nvmf_check_option(r, tok)) { \
@@ -396,7 +429,7 @@ static int __nvmf_supported_options(nvme_root_t r);
ret; \
})
-#define add_int_or_minus_one_argument(o, argstr, tok, arg) \
+#define add_int_or_minus_one_argument(r, argstr, tok, arg) \
({ \
int ret; \
if (nvmf_check_option(r, tok)) { \
@@ -552,6 +585,9 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr)
const char *hostnqn, *hostid, *hostkey, *ctrlkey;
bool discover = false, discovery_nqn = false;
nvme_root_t r = h->r;
+ long keyring_id = 0;
+ long key_id = 0;
+ int ret;
if (!transport) {
nvme_msg(h->r, LOG_ERR, "need a transport (-t) argument\n");
@@ -573,19 +609,37 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr)
errno = ENOMEM;
return -1;
}
+
if (!strcmp(nvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME)) {
nvme_ctrl_set_discovery_ctrl(c, true);
nvme_ctrl_set_unique_discovery_ctrl(c, false);
discovery_nqn = true;
}
+
if (nvme_ctrl_is_discovery_ctrl(c))
discover = true;
+
hostnqn = nvme_host_get_hostnqn(h);
hostid = nvme_host_get_hostid(h);
hostkey = nvme_host_get_dhchap_key(h);
if (!hostkey)
hostkey = nvme_ctrl_get_dhchap_host_key(c);
+
ctrlkey = nvme_ctrl_get_dhchap_key(c);
+
+ ret = __nvme_import_keys_from_config(h, c, &keyring_id, &key_id);
+ if (ret) {
+ errno = -ret;
+ return -1;
+ }
+
+ if (key_id == 0) {
+ if (cfg->tls_configured_key)
+ key_id = cfg->tls_configured_key;
+ else
+ key_id = cfg->tls_key;
+ }
+
if (add_argument(r, argstr, transport, transport) ||
add_argument(r, argstr, traddr,
nvme_ctrl_get_traddr(c)) ||
@@ -627,9 +681,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(r, argstr, tos, cfg->tos, true)) ||
- add_int_argument(r, argstr, keyring, cfg->keyring, false) ||
+ add_hex_argument(r, argstr, keyring, keyring_id, false) ||
(!strcmp(transport, "tcp") &&
- add_int_argument(r, argstr, tls_key, cfg->tls_key, false)) ||
+ add_hex_argument(r, argstr, tls_key, key_id, false)) ||
add_bool_argument(r, argstr, duplicate_connect,
cfg->duplicate_connect) ||
add_bool_argument(r, argstr, disable_sqflow,
@@ -760,7 +814,7 @@ static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr)
(int)strcspn(argstr,"\n"), argstr);
ret = write(fd, argstr, len);
if (ret != len) {
- nvme_msg(r, LOG_NOTICE, "Failed to write to %s: %s\n",
+ nvme_msg(r, LOG_INFO, "Failed to write to %s: %s\n",
nvmf_dev, strerror(errno));
switch (errno) {
case EALREADY:
@@ -777,6 +831,8 @@ static int __nvmf_add_ctrl(nvme_root_t r, const char *argstr)
return -ENVME_CONNECT_CONNREFUSED;
case EADDRNOTAVAIL:
return -ENVME_CONNECT_ADDRNOTAVAIL;
+ case ENOKEY:
+ return -ENVME_CONNECT_NOKEY;
default:
return -ENVME_CONNECT_WRITE;
}
@@ -864,6 +920,15 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c,
key = nvme_ctrl_get_dhchap_key(fc);
if (key)
nvme_ctrl_set_dhchap_key(c, key);
+ key = nvme_ctrl_get_keyring(fc);
+ if (key)
+ nvme_ctrl_set_keyring(c, key);
+ key = nvme_ctrl_get_tls_key_identity(fc);
+ if (key)
+ nvme_ctrl_set_tls_key_identity(c, key);
+ key = nvme_ctrl_get_tls_key(fc);
+ if (key)
+ nvme_ctrl_set_tls_key(c, key);
}
}
@@ -916,6 +981,24 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c,
return nvme_init_ctrl(h, c, ret);
}
+int nvmf_connect_ctrl(nvme_ctrl_t c)
+{
+ _cleanup_free_ char *argstr = NULL;
+ int ret;
+
+ ret = build_options(c->s->h, c, &argstr);
+ if (ret)
+ return ret;
+
+ ret = __nvmf_add_ctrl(c->s->h->r, argstr);
+ if (ret < 0) {
+ errno = -ret;
+ return -1;
+ }
+
+ return 0;
+}
+
nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h,
struct nvmf_disc_log_entry *e,
const struct nvme_fabrics_config *cfg,
diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h
index 8e26e9f..4da4388 100644
--- a/src/nvme/fabrics.h
+++ b/src/nvme/fabrics.h
@@ -37,6 +37,7 @@
* @tos: Type of service
* @keyring: Keyring to store and lookup keys
* @tls_key: TLS PSK for the connection
+ * @tls_configured_key: TLS PSK for connect command 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)
@@ -58,6 +59,7 @@ struct nvme_fabrics_config {
int tos;
long keyring;
long tls_key;
+ long tls_configured_key;
bool duplicate_connect;
bool disable_sqflow;
@@ -222,6 +224,17 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c,
const struct nvme_fabrics_config *cfg);
/**
+ * nvmf_connect_ctrl() - Connect a controller
+ * @c: Controller to be connected
+ *
+ * Issues a 'connect' command to the NVMe-oF controller.
+ * @c must be initialized and not connected to the topology.
+ *
+ * Return: 0 on success; on failure errno is set and -1 is returned.
+ */
+int nvmf_connect_ctrl(nvme_ctrl_t c);
+
+/**
* nvmf_get_discovery_log() - Return the discovery log page
* @c: Discovery controller to use
* @logp: Pointer to the log page to be returned
diff --git a/src/nvme/ioctl.c b/src/nvme/ioctl.c
index 9707829..ba9d5b1 100644
--- a/src/nvme/ioctl.c
+++ b/src/nvme/ioctl.c
@@ -625,45 +625,19 @@ int nvme_set_features_async_event(int fd, __u32 events,
int nvme_set_features_auto_pst(int fd, bool apste, bool save,
struct nvme_feat_auto_pst *apst, __u32 *result)
{
- struct nvme_set_features_args args = {
- .args_size = sizeof(args),
- .fd = fd,
- .fid = NVME_FEAT_FID_AUTO_PST,
- .nsid = NVME_NSID_NONE,
- .cdw11 = NVME_SET(!!apste, FEAT_APST_APSTE),
- .save = save,
- .uuidx = NVME_UUID_NONE,
- .data = apst,
- .data_len = sizeof(*apst),
- .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
- .result = result,
- };
-
- return nvme_set_features(&args);
+ return nvme_set_features_data(fd, NVME_FEAT_FID_AUTO_PST,
+ NVME_NSID_NONE, NVME_SET(!!apste, FEAT_APST_APSTE), save,
+ sizeof(*apst), apst, result);
}
int nvme_set_features_timestamp(int fd, bool save, __u64 timestamp)
{
__le64 t = cpu_to_le64(timestamp);
struct nvme_timestamp ts = {};
- struct nvme_set_features_args args = {
- .args_size = sizeof(args),
- .fd = fd,
- .fid = NVME_FEAT_FID_TIMESTAMP,
- .nsid = NVME_NSID_NONE,
- .cdw11 = 0,
- .cdw12 = 0,
- .save = save,
- .uuidx = NVME_UUID_NONE,
- .cdw15 = 0,
- .data_len = sizeof(ts),
- .data = &ts,
- .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
- .result = NULL,
- };
-
memcpy(ts.timestamp, &t, sizeof(ts.timestamp));
- return nvme_set_features(&args);
+
+ return nvme_set_features_data(fd, NVME_FEAT_FID_TIMESTAMP,
+ NVME_NSID_NONE, 0, save, sizeof(ts), &ts, NULL);
}
int nvme_set_features_hctm(int fd, __u16 tmt2, __u16 tmt1,
@@ -764,23 +738,8 @@ int nvme_set_features_lba_sts_interval(int fd, __u16 lsiri, __u16 lsipi,
int nvme_set_features_host_behavior(int fd, bool save,
struct nvme_feat_host_behavior *data)
{
- struct nvme_set_features_args args = {
- .args_size = sizeof(args),
- .fd = fd,
- .fid = NVME_FEAT_FID_HOST_BEHAVIOR,
- .nsid = NVME_NSID_NONE,
- .cdw11 = 0,
- .cdw12 = 0,
- .save = false,
- .uuidx = NVME_UUID_NONE,
- .cdw15 = 0,
- .data_len = sizeof(*data),
- .data = data,
- .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
- .result = NULL,
- };
-
- return nvme_set_features(&args);
+ return nvme_set_features_data(fd, NVME_FEAT_FID_HOST_BEHAVIOR,
+ NVME_NSID_NONE, 0, false, sizeof(*data), data, NULL);
}
int nvme_set_features_sanitize(int fd, bool nodrm, bool save, __u32 *result)
@@ -809,23 +768,9 @@ int nvme_set_features_host_id(int fd, bool exhid, bool save, __u8 *hostid)
{
__u32 len = exhid ? 16 : 8;
__u32 value = !!exhid;
- struct nvme_set_features_args args = {
- .args_size = sizeof(args),
- .fd = fd,
- .fid = NVME_FEAT_FID_HOST_ID,
- .nsid = NVME_NSID_NONE,
- .cdw11 = value,
- .cdw12 = 0,
- .save = save,
- .uuidx = NVME_UUID_NONE,
- .cdw15 = 0,
- .data_len = len,
- .data = hostid,
- .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
- .result = NULL,
- };
- return nvme_set_features(&args);
+ return nvme_set_features_data(fd, NVME_FEAT_FID_HOST_ID,
+ NVME_NSID_NONE, value, save, len, hostid, NULL);
}
int nvme_set_features_resv_mask(int fd, __u32 mask, bool save, __u32 *result)
@@ -1696,12 +1641,25 @@ int nvme_get_property(struct nvme_get_property_args *args)
int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args)
{
- __u32 cdw10 = NVME_SET(args->sanact, SANITIZE_CDW10_SANACT) |
- NVME_SET(!!args->ause, SANITIZE_CDW10_AUSE) |
- NVME_SET(args->owpass, SANITIZE_CDW10_OWPASS) |
- NVME_SET(!!args->oipbp, SANITIZE_CDW10_OIPBP) |
- NVME_SET(!!args->nodas, SANITIZE_CDW10_NODAS);
- __u32 cdw11 = args->ovrpat;
+ const size_t size_v1 = sizeof_args(struct nvme_sanitize_nvm_args, nodas, __u64);
+ const size_t size_v2 = sizeof_args(struct nvme_sanitize_nvm_args, emvs, __u64);
+ __u32 cdw10, cdw11;
+
+ if (args->args_size < size_v1 || args->args_size > size_v2) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cdw10 = NVME_SET(args->sanact, SANITIZE_CDW10_SANACT) |
+ NVME_SET(!!args->ause, SANITIZE_CDW10_AUSE) |
+ NVME_SET(args->owpass, SANITIZE_CDW10_OWPASS) |
+ NVME_SET(!!args->oipbp, SANITIZE_CDW10_OIPBP) |
+ NVME_SET(!!args->nodas, SANITIZE_CDW10_NODAS);
+
+ if (args->args_size == size_v2)
+ cdw10 |= NVME_SET(!!args->emvs, SANITIZE_CDW10_EMVS);
+
+ cdw11 = args->ovrpat;
struct nvme_passthru_cmd cmd = {
.opcode = nvme_admin_sanitize_nvm,
@@ -1710,10 +1668,6 @@ int nvme_sanitize_nvm(struct nvme_sanitize_nvm_args *args)
.timeout_ms = args->timeout,
};
- if (args->args_size < sizeof(*args)) {
- errno = EINVAL;
- return -1;
- }
return nvme_submit_admin_passthru(args->fd, &cmd, args->result);
}
diff --git a/src/nvme/ioctl.h b/src/nvme/ioctl.h
index 2ebd39c..7d407a0 100644
--- a/src/nvme/ioctl.h
+++ b/src/nvme/ioctl.h
@@ -285,11 +285,13 @@ enum nvme_cmd_dword_fields {
NVME_SANITIZE_CDW10_OWPASS_SHIFT = 4,
NVME_SANITIZE_CDW10_OIPBP_SHIFT = 8,
NVME_SANITIZE_CDW10_NODAS_SHIFT = 9,
+ NVME_SANITIZE_CDW10_EMVS_SHIFT = 10,
NVME_SANITIZE_CDW10_SANACT_MASK = 0x7,
NVME_SANITIZE_CDW10_AUSE_MASK = 0x1,
NVME_SANITIZE_CDW10_OWPASS_MASK = 0xf,
NVME_SANITIZE_CDW10_OIPBP_MASK = 0x1,
NVME_SANITIZE_CDW10_NODAS_MASK = 0x1,
+ NVME_SANITIZE_CDW10_EMVS_MASK = 0x1,
NVME_SECURITY_NSSF_SHIFT = 0,
NVME_SECURITY_SPSP0_SHIFT = 8,
NVME_SECURITY_SPSP1_SHIFT = 16,
@@ -2311,6 +2313,38 @@ static inline int nvme_get_log_persistent_event(int fd,
}
/**
+ * nvme_get_log_lockdown() - Retrieve lockdown Log
+ * @fd: File descriptor of nvme device
+ * @cnscp: Contents and Scope of Command and Feature Identifier Lists
+ * @lockdown_log: Buffer to store the lockdown log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_get_log_lockdown(int fd,
+ __u8 cnscp, struct nvme_lockdown_log *lockdown_log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = lockdown_log,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN,
+ .len = sizeof(*lockdown_log),
+ .nsid = NVME_NSID_ALL,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = cnscp,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, &args);
+}
+
+/**
* nvme_set_features() - Set a feature attribute
* @args: &struct nvme_set_features_args argument structure
*
@@ -2350,7 +2384,7 @@ static inline int nvme_set_features_data(int fd, __u8 fid, __u32 nsid,
.cdw15 = 0,
.data_len = data_len,
.save = save,
- .uuidx = 0,
+ .uuidx = NVME_UUID_NONE,
.fid = fid,
};
return nvme_set_features(&args);
diff --git a/src/nvme/json.c b/src/nvme/json.c
index 2c769f2..af28bd4 100644
--- a/src/nvme/json.c
+++ b/src/nvme/json.c
@@ -25,62 +25,10 @@
#define JSON_UPDATE_BOOL_OPTION(c, k, a, o) \
if (!strcmp(# a, k ) && !c->a) c->a = json_object_get_boolean(o);
-static void json_import_nvme_tls_key(nvme_ctrl_t c, const char *keyring_str,
- const char *encoded_key)
-{
- struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c);
- const char *hostnqn = nvme_host_get_hostnqn(c->s->h);
- const char *subsysnqn = nvme_ctrl_get_subsysnqn(c);
- int key_len;
- unsigned int hmac;
- long key_id;
- _cleanup_free_ unsigned char *key_data = NULL;
-
- if (!hostnqn || !subsysnqn) {
- nvme_msg(NULL, LOG_ERR, "Invalid NQNs (%s, %s)\n",
- hostnqn, subsysnqn);
- return;
- }
- key_data = nvme_import_tls_key(encoded_key, &key_len, &hmac);
- if (!key_data) {
- nvme_msg(NULL, LOG_ERR, "Failed to decode TLS Key '%s'\n",
- encoded_key);
- return;
- }
- key_id = nvme_insert_tls_key_versioned(keyring_str, "psk",
- hostnqn, subsysnqn,
- 0, hmac, key_data, key_len);
- if (key_id <= 0)
- nvme_msg(NULL, LOG_ERR, "Failed to insert TLS KEY, error %d\n",
- errno);
- else {
- cfg->tls_key = key_id;
- cfg->tls = true;
- }
-}
-
-static void json_export_nvme_tls_key(long keyring_id, long tls_key,
- struct json_object *obj)
-{
- int key_len;
- _cleanup_free_ unsigned char *key_data = NULL;
-
- key_data = nvme_read_key(keyring_id, tls_key, &key_len);
- if (key_data) {
- _cleanup_free_ char *tls_str = NULL;
-
- tls_str = nvme_export_tls_key(key_data, key_len);
- if (tls_str)
- json_object_object_add(obj, "tls_key",
- json_object_new_string(tls_str));
- }
-}
-
static void json_update_attributes(nvme_ctrl_t c,
struct json_object *ctrl_obj)
{
struct nvme_fabrics_config *cfg = nvme_ctrl_get_config(c);
- const char *keyring_str = NULL, *encoded_key = NULL;
json_object_object_foreach(ctrl_obj, key_str, val_obj) {
JSON_UPDATE_INT_OPTION(cfg, key_str,
@@ -120,31 +68,18 @@ 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_str = json_object_get_string(val_obj);
- keyring = nvme_lookup_keyring(keyring_str);
- if (keyring) {
- cfg->keyring = keyring;
- nvme_set_keyring(cfg->keyring);
- }
+ if (!strcmp("keyring", key_str))
+ nvme_ctrl_set_keyring(c,
+ json_object_get_string(val_obj));
+ if (!strcmp("tls_key_identity", key_str)) {
+ nvme_ctrl_set_tls_key_identity(c,
+ json_object_get_string(val_obj));
+ }
+ if (!strcmp("tls_key", key_str)) {
+ nvme_ctrl_set_tls_key(c,
+ json_object_get_string(val_obj));
}
- if (!strcmp("tls_key", key_str) && cfg->tls_key == 0)
- encoded_key = json_object_get_string(val_obj);
}
-
- /*
- * We might need the keyring information from the above loop,
- * so we can only import the TLS key once all entries are
- * processed.
- */
- if (encoded_key)
- json_import_nvme_tls_key(c, keyring_str, encoded_key);
}
static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj)
@@ -181,6 +116,19 @@ static void json_parse_port(nvme_subsystem_t s, struct json_object *port_obj)
attr_obj = json_object_object_get(port_obj, "dhchap_ctrl_key");
if (attr_obj)
nvme_ctrl_set_dhchap_key(c, json_object_get_string(attr_obj));
+ attr_obj = json_object_object_get(port_obj, "keyring");
+ if (attr_obj)
+ nvme_ctrl_set_keyring(c, json_object_get_string(attr_obj));
+ attr_obj = json_object_object_get(port_obj, "tls_key_identity");
+ if (attr_obj) {
+ nvme_ctrl_set_tls_key_identity(c,
+ json_object_get_string(attr_obj));
+ }
+ attr_obj = json_object_object_get(port_obj, "tls_key");
+ if (attr_obj) {
+ nvme_ctrl_set_tls_key(c,
+ json_object_get_string(attr_obj));
+ }
}
static void json_parse_subsys(nvme_host_t h, struct json_object *subsys_obj)
@@ -368,6 +316,19 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c)
if (value)
json_object_object_add(port_obj, "dhchap_ctrl_key",
json_object_new_string(value));
+ JSON_BOOL_OPTION(cfg, port_obj, tls);
+ value = nvme_ctrl_get_keyring(c);
+ if (value)
+ json_object_object_add(port_obj, "keyring",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_tls_key_identity(c);
+ if (value)
+ json_object_object_add(port_obj, "tls_key_identity",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_tls_key(c);
+ if (value)
+ json_object_object_add(port_obj, "tls_key",
+ json_object_new_string(value));
JSON_INT_OPTION(cfg, port_obj, nr_io_queues, 0);
JSON_INT_OPTION(cfg, port_obj, nr_write_queues, 0);
JSON_INT_OPTION(cfg, port_obj, nr_poll_queues, 0);
@@ -384,7 +345,6 @@ static void json_update_port(struct json_object *ctrl_array, nvme_ctrl_t c)
JSON_BOOL_OPTION(cfg, port_obj, disable_sqflow);
JSON_BOOL_OPTION(cfg, port_obj, hdr_digest);
JSON_BOOL_OPTION(cfg, port_obj, data_digest);
- JSON_BOOL_OPTION(cfg, port_obj, tls);
JSON_BOOL_OPTION(cfg, port_obj, concat);
if (nvme_ctrl_is_persistent(c))
json_object_object_add(port_obj, "persistent",
@@ -392,23 +352,6 @@ 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) {
- _cleanup_free_ char *desc =
- nvme_describe_key_serial(cfg->keyring);
-
- if (desc) {
- json_object_object_add(port_obj, "keyring",
- json_object_new_string(desc));
- }
- }
- /*
- * Store the TLS key in PSK interchange format
- */
- if (cfg->tls_key)
- json_export_nvme_tls_key(cfg->keyring, cfg->tls_key, port_obj);
json_object_array_add(ctrl_array, port_obj);
}
@@ -491,11 +434,14 @@ int json_update_config(nvme_root_t r, const char *config_file)
}
}
if (!config_file) {
- ret = json_object_to_fd(1, json_root, JSON_C_TO_STRING_PRETTY);
+ ret = json_object_to_fd(1, json_root,
+ JSON_C_TO_STRING_PRETTY |
+ JSON_C_TO_STRING_NOSLASHESCAPE);
printf("\n");
} else
ret = json_object_to_file_ext(config_file, json_root,
- JSON_C_TO_STRING_PRETTY);
+ JSON_C_TO_STRING_PRETTY |
+ JSON_C_TO_STRING_NOSLASHESCAPE);
if (ret < 0) {
nvme_msg(r, LOG_ERR, "Failed to write to %s, %s\n",
config_file ? "stdout" : config_file,
@@ -564,9 +510,18 @@ static void json_dump_ctrl(struct json_object *ctrl_array, nvme_ctrl_t c)
if (!strcmp(transport, "tcp")) {
JSON_BOOL_OPTION(cfg, ctrl_obj, tls);
- if (cfg->tls_key)
- json_export_nvme_tls_key(cfg->keyring, cfg->tls_key,
- ctrl_obj);
+ value = nvme_ctrl_get_keyring(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "keyring",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_tls_key_identity(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "tls_key_identity",
+ json_object_new_string(value));
+ value = nvme_ctrl_get_tls_key(c);
+ if (value)
+ json_object_object_add(ctrl_obj, "tls_key",
+ json_object_new_string(value));
}
JSON_BOOL_OPTION(cfg, ctrl_obj, concat);
if (nvme_ctrl_is_persistent(c))
@@ -640,7 +595,9 @@ int json_dump_tree(nvme_root_t r)
}
json_object_object_add(json_root, "hosts", host_array);
- ret = json_object_to_fd(r->log.fd, json_root, JSON_C_TO_STRING_PRETTY);
+ ret = json_object_to_fd(r->log.fd, json_root,
+ JSON_C_TO_STRING_PRETTY |
+ JSON_C_TO_STRING_NOSLASHESCAPE);
if (ret < 0) {
nvme_msg(r, LOG_ERR, "Failed to write, %s\n",
json_util_get_last_err());
diff --git a/src/nvme/linux.c b/src/nvme/linux.c
index aff0544..e74fac2 100644
--- a/src/nvme/linux.c
+++ b/src/nvme/linux.c
@@ -525,6 +525,11 @@ char *nvme_get_path_attr(nvme_path_t p, const char *attr)
}
#ifndef CONFIG_OPENSSL
+static unsigned char default_hmac(size_t key_len)
+{
+ return NVME_HMAC_ALG_NONE;
+}
+
int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
unsigned int key_len, unsigned char *secret,
unsigned char *key)
@@ -552,7 +557,7 @@ static int derive_retained_key(int hmac, const char *hostnqn,
}
static int derive_psk_digest(const char *hostnqn, const char *subsysnqn,
- int version, int hmac,
+ int version, int cipher,
unsigned char *retained, size_t key_len,
char *digest, size_t digest_len)
{
@@ -562,7 +567,7 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn,
return -1;
}
-static int derive_tls_key(int version, int hmac, const char *context,
+static int derive_tls_key(int version, int cipher, const char *context,
unsigned char *retained,
unsigned char *psk, size_t key_len)
{
@@ -572,20 +577,41 @@ static int derive_tls_key(int version, int hmac, const char *context,
return -1;
}
#else /* CONFIG_OPENSSL */
-static const EVP_MD *select_hmac(int hmac, size_t *key_len)
+static unsigned char default_hmac(size_t key_len)
+{
+ unsigned char hmac = NVME_HMAC_ALG_NONE;
+
+ switch (key_len) {
+ case 32:
+ hmac = NVME_HMAC_ALG_SHA2_256;
+ break;
+ case 48:
+ hmac = NVME_HMAC_ALG_SHA2_384;
+ break;
+ case 64:
+ hmac = NVME_HMAC_ALG_SHA2_512;
+ break;
+ default:
+ break;
+ }
+ return hmac;
+}
+
+static const EVP_MD *select_hmac(int hmac, size_t *hmac_len)
{
const EVP_MD *md = NULL;
switch (hmac) {
case NVME_HMAC_ALG_SHA2_256:
md = EVP_sha256();
- *key_len = 32;
+ *hmac_len = 32;
break;
case NVME_HMAC_ALG_SHA2_384:
md = EVP_sha384();
- *key_len = 48;
+ *hmac_len = 48;
break;
default:
+ *hmac_len = 0;
break;
}
return md;
@@ -595,18 +621,51 @@ static DEFINE_CLEANUP_FUNC(
cleanup_evp_pkey_ctx, EVP_PKEY_CTX *, EVP_PKEY_CTX_free)
#define _cleanup_evp_pkey_ctx_ __cleanup__(cleanup_evp_pkey_ctx)
+/*
+ * derive_retained_key()
+ *
+ * Derive a retained key according to NVMe TCP Transport specification:
+ *
+ * The retained PSK is derived from the configured PSK. The configured PSK
+ * shall be destroyed as soon as the retained PSK is generated and stored.
+ * Each NVMe/TCP entity shall support:
+ * 1) transforming the configured PSK into a retained PSK before it is stored
+ * by the NVMe/TCP entity for repeated use with another NVMe/TCP entity; and
+ * 2) using the configured PSK as a retained PSK.
+ *
+ * The method to derive a retained PSK from a configured PSK shall be using
+ * the HKDF-Extract and HKDF-Expand-Label operations (refer to RFC 5869 and
+ * RFC 8446):
+ * 1. PRK = HKDF-Extract(0, Configured PSK); and
+ * 2. Retained PSK = HKDF-Expand-Label(PRK, “HostNQN”, NQNh,
+ * Length(Configured PSK)),
+ * where NQNh is the NQN of the host.
+ *
+ * 'hmac' indicates the hash function to be used to transform the configured
+ * PSK in a retained PSK, encoded as follows:
+ *
+ * - 0 indicates no transform (i.e., the configured PSK is used as a
+ * retained PSK)
+ * - 1 indicates SHA-256
+ * - 2 indicates SHA-384
+ */
static int derive_retained_key(int hmac, const char *hostnqn,
- unsigned char *generated,
+ unsigned char *configured,
unsigned char *retained,
size_t key_len)
{
- const EVP_MD *md;
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
uint16_t length = key_len & 0xFFFF;
+ const EVP_MD *md;
size_t hmac_len;
+ if (hmac == NVME_HMAC_ALG_NONE) {
+ memcpy(retained, configured, key_len);
+ return key_len;
+ }
+
md = select_hmac(hmac, &hmac_len);
- if (!md || hmac_len > key_len) {
+ if (!md || !hmac_len) {
errno = EINVAL;
return -1;
}
@@ -625,7 +684,7 @@ static int derive_retained_key(int hmac, const char *hostnqn,
errno = ENOKEY;
return -1;
}
- if (EVP_PKEY_CTX_set1_hkdf_key(ctx, generated, key_len) <= 0) {
+ if (EVP_PKEY_CTX_set1_hkdf_key(ctx, configured, key_len) <= 0) {
errno = ENOKEY;
return -1;
}
@@ -658,17 +717,39 @@ static int derive_retained_key(int hmac, const char *hostnqn,
return key_len;
}
-static int derive_tls_key(int version, int hmac, const char *context,
- unsigned char *retained,
+/*
+ * derive_tls_key()
+ *
+ * Derive a TLS PSK from a retained PSK.
+ *
+ * The TLS PSK shall be derived as follows from an input PSK (i.e., either
+ * a retained PSK or a generated PSK) and a PSK identity using the HKDF-Extract
+ * and HKDF-Expand-Label operations (refer to RFC 5869 and RFC 8446) where the
+ * hash function is the one specified by the hash specifier of the PSK identity:
+ * 1. PRK = HKDF-Extract(0, Input PSK); and
+ * 2. TLS PSK = HKDF-Expand-Label(PRK, “nvme-tls-psk”, PskIdentity, L),
+ * where PskIdentity is the PSK identity and L is the output size in bytes of
+ * the hash function (i.e., 32 for SHA-256 and 48 for SHA-384).
+ *
+ * Note that this is _not_ the hash value as specified by the configured key,
+ * but rather the hash function of the cipher suite associated with the
+ * PSK:
+ * - 1 indicates SHA-245 (for the TLS_AES_128_GCM_SHA256 cipher suite)
+ * - 2 indicates SHA-384 (for the TLS_AES_256_GCM_SHA384 cipher suite)
+ *
+ * and the value '0' is invalid here.
+ */
+static int derive_tls_key(int version, unsigned char cipher,
+ const char *context, unsigned char *retained,
unsigned char *psk, size_t key_len)
{
- const EVP_MD *md;
_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
- size_t hmac_len;
uint16_t length = key_len & 0xFFFF;
+ const EVP_MD *md;
+ size_t hmac_len;
- md = select_hmac(hmac, &hmac_len);
- if (!md || hmac_len > key_len) {
+ md = select_hmac(cipher, &hmac_len);
+ if (!md || !hmac_len) {
errno = EINVAL;
return -1;
}
@@ -707,9 +788,9 @@ static int derive_tls_key(int version, int hmac, const char *context,
return -1;
}
if (version == 1) {
- char hash_str[4];
+ char hash_str[5];
- sprintf(hash_str, "%02d ", hmac);
+ sprintf(hash_str, "%02d ", cipher);
if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
(const unsigned char *)hash_str,
strlen(hash_str)) <= 0) {
@@ -805,10 +886,10 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn,
char *digest, size_t digest_len)
{
static const char hmac_seed[] = "NVMe-over-Fabrics";
- size_t hmac_len;
const EVP_MD *md = select_hmac(hmac, &hmac_len);
_cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx = NULL;
_cleanup_free_ unsigned char *psk_ctx = NULL;
+ size_t hmac_len;
size_t len;
hmac_ctx = HMAC_CTX_new();
@@ -816,7 +897,8 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn,
errno = ENOMEM;
return -1;
}
- if (!md) {
+ md = select_hmac(cipher, &hmac_len);
+ if (!md || !hmac_len) {
errno = EINVAL;
return -1;
}
@@ -886,10 +968,10 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
unsigned char *key)
{
const char hmac_seed[] = "NVMe-over-Fabrics";
- OSSL_PARAM params[2], *p = params;
_cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL;
_cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL;
_cleanup_evp_mac_ EVP_MAC *mac = NULL;
+ OSSL_PARAM params[2], *p = params;
char *progq = NULL;
char *digest;
size_t len;
@@ -965,19 +1047,19 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
}
static int derive_psk_digest(const char *hostnqn, const char *subsysnqn,
- int version, int hmac,
+ int version, int cipher,
unsigned char *retained, size_t key_len,
char *digest, size_t digest_len)
{
static const char hmac_seed[] = "NVMe-over-Fabrics";
- size_t hmac_len;
- OSSL_PARAM params[2], *p = params;
_cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL;
_cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL;
+ _cleanup_free_ unsigned char *psk_ctx = NULL;
_cleanup_evp_mac_ EVP_MAC *mac = NULL;
+ OSSL_PARAM params[2], *p = params;
+ size_t hmac_len;
char *progq = NULL;
char *dig = NULL;
- _cleanup_free_ unsigned char *psk_ctx = NULL;
size_t len;
lib_ctx = OSSL_LIB_CTX_new();
@@ -996,7 +1078,7 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn,
errno = ENOMEM;
return -1;
}
- switch (hmac) {
+ switch (cipher) {
case NVME_HMAC_ALG_SHA2_256:
dig = OSSL_DIGEST_NAME_SHA2_256;
break;
@@ -1069,12 +1151,12 @@ static int derive_psk_digest(const char *hostnqn, const char *subsysnqn,
#endif /* !CONFIG_OPENSSL_3 */
static int gen_tls_identity(const char *hostnqn, const char *subsysnqn,
- int version, int hmac, char *digest,
+ int version, int cipher, char *digest,
char *identity)
{
if (version == 0) {
sprintf(identity, "NVMe%01dR%02d %s %s",
- version, hmac, hostnqn, subsysnqn);
+ version, cipher, hostnqn, subsysnqn);
return strlen(identity);
}
if (version > 1) {
@@ -1083,7 +1165,7 @@ static int gen_tls_identity(const char *hostnqn, const char *subsysnqn,
}
sprintf(identity, "NVMe%01dR%02d %s %s %s",
- version, hmac, hostnqn, subsysnqn, digest);
+ version, cipher, hostnqn, subsysnqn, digest);
return strlen(identity);
}
@@ -1095,6 +1177,7 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
_cleanup_free_ unsigned char *retained = NULL;
_cleanup_free_ char *digest = NULL;
char *context = identity;
+ unsigned char cipher;
int ret = -1;
if (!hostnqn || !subsysnqn || !identity || !psk) {
@@ -1111,6 +1194,11 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
if (ret < 0)
return ret;
+ if (hmac == NVME_HMAC_ALG_NONE)
+ cipher = default_hmac(key_len);
+ else
+ cipher = hmac;
+
if (version == 1) {
size_t digest_len = 2 * key_len;
@@ -1119,25 +1207,25 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn,
errno = ENOMEM;
return -1;
}
- ret = derive_psk_digest(hostnqn, subsysnqn, version, hmac,
+ ret = derive_psk_digest(hostnqn, subsysnqn, version, cipher,
retained, key_len,
digest, digest_len);
- if (ret)
+ if (ret < 0)
return ret;
context = digest;
}
- ret = gen_tls_identity(hostnqn, subsysnqn, version, hmac,
+ ret = gen_tls_identity(hostnqn, subsysnqn, version, cipher,
digest, identity);
if (ret < 0)
return ret;
- return derive_tls_key(version, hmac, context, retained,
+ return derive_tls_key(version, cipher, context, retained,
psk, key_len);
}
-static size_t nvme_identity_len(int hmac, int version, const char *hostnqn,
- const char *subsysnqn)
+static ssize_t nvme_identity_len(int hmac, int version, const char *hostnqn,
+ const char *subsysnqn)
{
- size_t len;
+ ssize_t len;
if (!hostnqn || !subsysnqn) {
errno = EINVAL;
@@ -1160,9 +1248,9 @@ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn,
int version, int hmac,
unsigned char *configured_key, int key_len)
{
- char *identity;
- size_t identity_len;
_cleanup_free_ unsigned char *psk = NULL;
+ char *identity;
+ ssize_t identity_len;
int ret = -1;
identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn);
@@ -1203,11 +1291,21 @@ long nvme_lookup_keyring(const char *keyring)
char *nvme_describe_key_serial(long key_id)
{
- char *desc;
+ _cleanup_free_ char *str = NULL;
+ char *last;
+
+ if (keyctl_describe_alloc(key_id, &str) < 0)
+ return NULL;
+
+ last = strrchr(str, ';');
+ if (!last)
+ return NULL;
- if (keyctl_describe_alloc(key_id, &desc) < 0)
- desc = NULL;
- return desc;
+ last++;
+ if (strlen(last) == 0)
+ return NULL;
+
+ return strdup(last);
}
long nvme_lookup_key(const char *type, const char *identity)
@@ -1337,27 +1435,17 @@ int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb,
return ret;
}
-long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
- const char *hostnqn, const char *subsysnqn,
- int version, int hmac,
- unsigned char *configured_key, int key_len)
+static long __nvme_insert_tls_key_versioned(key_serial_t keyring_id, const char *key_type,
+ const char *hostnqn, const char *subsysnqn,
+ int version, int hmac,
+ unsigned char *configured_key, int key_len)
{
- key_serial_t keyring_id, key;
- _cleanup_free_ char *identity = NULL;
- size_t identity_len;
_cleanup_free_ unsigned char *psk = NULL;
+ _cleanup_free_ char *identity = NULL;
+ ssize_t identity_len;
+ key_serial_t key;
int ret;
- keyring_id = nvme_lookup_keyring(keyring);
- if (keyring_id == 0) {
- errno = ENOKEY;
- return 0;
- }
-
- ret = nvme_set_keyring(keyring_id);
- if (ret < 0)
- return 0;
-
identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn);
if (identity_len < 0)
return 0;
@@ -1387,6 +1475,29 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
return key;
}
+long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
+ const char *hostnqn, const char *subsysnqn,
+ int version, int hmac,
+ unsigned char *configured_key, int key_len)
+{
+ key_serial_t keyring_id;
+ int ret;
+
+ keyring_id = nvme_lookup_keyring(keyring);
+ if (keyring_id == 0) {
+ errno = ENOKEY;
+ return 0;
+ }
+
+ ret = nvme_set_keyring(keyring_id);
+ if (ret < 0)
+ return 0;
+ return __nvme_insert_tls_key_versioned(keyring_id, key_type,
+ hostnqn, subsysnqn,
+ version, hmac,
+ configured_key, key_len);
+}
+
long nvme_revoke_tls_key(const char *keyring, const char *key_type,
const char *identity)
{
@@ -1405,6 +1516,94 @@ long nvme_revoke_tls_key(const char *keyring, const char *key_type,
return keyctl_revoke(key);
}
+
+static int __nvme_insert_tls_key(long keyring_id,
+ const char *hostnqn, const char *subsysnqn,
+ const char *identity, const char *key)
+{
+ _cleanup_free_ unsigned char *key_data = NULL;
+ unsigned char version;
+ unsigned char hmac;
+ size_t key_len;
+
+ key_data = nvme_import_tls_key_versioned(key, &version,
+ &hmac, &key_len);
+ if (!key_data)
+ return -EINVAL;
+
+ if (hmac == NVME_HMAC_ALG_NONE || !identity) {
+ /*
+ * This is a configured key (hmac 0) or we don't know the
+ * identity and so the assumtion is it is also a
+ * configured key. Derive a new key and load the newly
+ * created key into the keystore.
+ */
+ return __nvme_insert_tls_key_versioned(keyring_id, "psk",
+ hostnqn, subsysnqn,
+ version, hmac,
+ key_data, key_len);
+ }
+
+ return nvme_update_key(keyring_id, "psk", identity,
+ key_data, key_len);
+}
+
+int __nvme_import_keys_from_config(nvme_host_t h, nvme_ctrl_t c,
+ long *keyring_id, long *key_id)
+{
+ const char *hostnqn = nvme_host_get_hostnqn(h);
+ const char *subsysnqn = nvme_ctrl_get_subsysnqn(c);
+ const char *keyring, *key, *identity;
+ long kr_id, id = 0;
+
+ if (!hostnqn || !subsysnqn) {
+ nvme_msg(h->r, LOG_ERR, "Invalid NQNs (%s, %s)\n",
+ hostnqn, subsysnqn);
+ return -EINVAL;
+ }
+
+ keyring = nvme_ctrl_get_keyring(c);
+ if (keyring)
+ kr_id = nvme_lookup_keyring(keyring);
+ else
+ kr_id = c->cfg.keyring;
+
+ /*
+ * Fallback to the default keyring. Note this will also add the
+ * keyring to connect command line and to the JSON config output.
+ * That means we are explicitly selecting the keyring.
+ */
+ if (!kr_id)
+ kr_id = nvme_lookup_keyring(".nvme");
+
+ if (nvme_set_keyring(kr_id) < 0) {
+ nvme_msg(h->r, LOG_ERR, "Failed to set keyring\n");
+ return -errno;
+ }
+
+ key = nvme_ctrl_get_tls_key(c);
+ if (!key)
+ return 0;
+
+ identity = nvme_ctrl_get_tls_key_identity(c);
+ if (identity)
+ id = nvme_lookup_key("psk", identity);
+
+ if (!id)
+ id = __nvme_insert_tls_key(kr_id, hostnqn,
+ subsysnqn, identity, key);
+
+ if (id <= 0) {
+ nvme_msg(h->r, LOG_ERR, "Failed to insert TLS KEY, error %d\n",
+ errno);
+ return -errno;
+ }
+
+ *keyring_id = kr_id;
+ *key_id = id;
+
+ return 0;
+}
#else
long nvme_lookup_keyring(const char *keyring)
{
@@ -1478,6 +1677,12 @@ long nvme_revoke_tls_key(const char *keyring, const char *key_type,
errno = ENOTSUP;
return -1;
}
+
+int __nvme_import_keys_from_config(nvme_host_t h, nvme_ctrl_t c,
+ long *keyring_id, long *key_id)
+{
+ return -ENOTSUP;
+}
#endif
long nvme_insert_tls_key(const char *keyring, const char *key_type,
@@ -1489,21 +1694,44 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type,
configured_key, key_len);
}
-char *nvme_export_tls_key(const unsigned char *key_data, int key_len)
+/*
+ * PSK Interchange Format
+ * NVMeTLSkey-<v>:<xx>:<s>:
+ *
+ * x: version as one ASCII char
+ * yy: hmac encoded as two ASCII chars
+ * 00: no transform ('configured PSK')
+ * 01: SHA-256
+ * 02: SHA-384
+ * s: 32 or 48 bytes binary followed by a CRC-32 of the configured PSK
+ * (4 bytes) encoded as base64
+ */
+char *nvme_export_tls_key_versioned(unsigned char version, unsigned char hmac,
+ const unsigned char *key_data,
+ size_t key_len)
{
- unsigned char raw_secret[52];
- char *encoded_key;
unsigned int raw_len, encoded_len, len;
unsigned long crc = crc32(0L, NULL, 0);
+ unsigned char raw_secret[52];
+ char *encoded_key;
- if (key_len == 32) {
- raw_len = 32;
- } else if (key_len == 48) {
- raw_len = 48;
- } else {
- errno = EINVAL;
- return NULL;
+ switch (hmac) {
+ case NVME_HMAC_ALG_NONE:
+ if (key_len != 32 && key_len != 48)
+ goto err_inval;
+ break;
+ case NVME_HMAC_ALG_SHA2_256:
+ if (key_len != 32)
+ goto err_inval;
+ break;
+ case NVME_HMAC_ALG_SHA2_384:
+ if (key_len != 48)
+ goto err_inval;
+ break;
+ default:
+ goto err_inval;
}
+ raw_len = key_len;
memcpy(raw_secret, key_data, raw_len);
crc = crc32(crc, raw_secret, raw_len);
@@ -1519,48 +1747,75 @@ char *nvme_export_tls_key(const unsigned char *key_data, int key_len)
return NULL;
}
memset(encoded_key, 0, encoded_len);
- len = sprintf(encoded_key, "NVMeTLSkey-1:%02x:",
- key_len == 32 ? 1 : 2);
+ len = sprintf(encoded_key, "NVMeTLSkey-%x:%02x:", version, hmac);
len += base64_encode(raw_secret, raw_len, encoded_key + len);
encoded_key[len++] = ':';
encoded_key[len++] = '\0';
return encoded_key;
+
+err_inval:
+ errno = EINVAL;
+ return NULL;
+
}
-unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len,
- unsigned int *hmac)
+char *nvme_export_tls_key(const unsigned char *key_data, int key_len)
+{
+ unsigned char hmac;
+
+ if (key_len == 32)
+ hmac = NVME_HMAC_ALG_SHA2_256;
+ else
+ hmac = NVME_HMAC_ALG_SHA2_384;
+
+ return nvme_export_tls_key_versioned(1, hmac, key_data, key_len);
+}
+
+unsigned char *nvme_import_tls_key_versioned(const char *encoded_key,
+ unsigned char *version,
+ unsigned char *hmac,
+ size_t *key_len)
{
unsigned char decoded_key[128], *key_data;
unsigned int crc = crc32(0L, NULL, 0);
unsigned int key_crc;
- int err, decoded_len;
+ int err, _version, _hmac, decoded_len;
+ size_t len;
- if (sscanf(encoded_key, "NVMeTLSkey-1:%02x:*s", &err) != 1) {
+ if (sscanf(encoded_key, "NVMeTLSkey-%d:%02x:*s",
+ &_version, &_hmac) != 2) {
errno = EINVAL;
return NULL;
}
- switch (err) {
- case 1:
- if (strlen(encoded_key) != 65) {
- errno = EINVAL;
- return NULL;
- }
+
+ if (_version != 1) {
+ errno = EINVAL;
+ return NULL;
+ }
+ *version = _version;
+
+ len = strlen(encoded_key);
+ switch (_hmac) {
+ case NVME_HMAC_ALG_NONE:
+ if (len != 65 && len != 89)
+ goto err_inval;
break;
- case 2:
- if (strlen(encoded_key) != 89) {
- errno = EINVAL;
- return NULL;
- }
+ case NVME_HMAC_ALG_SHA2_256:
+ if (len != 65)
+ goto err_inval;
+ break;
+ case NVME_HMAC_ALG_SHA2_384:
+ if (len != 89)
+ goto err_inval;
break;
default:
errno = EINVAL;
return NULL;
}
+ *hmac = _hmac;
- *hmac = err;
- err = base64_decode(encoded_key + 16, strlen(encoded_key) - 17,
- decoded_key);
+ err = base64_decode(encoded_key + 16, len - 17, decoded_key);
if (err < 0) {
errno = ENOKEY;
return NULL;
@@ -1592,4 +1847,25 @@ unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len,
*key_len = decoded_len;
return key_data;
+
+err_inval:
+ errno = EINVAL;
+ return NULL;
+}
+
+unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len,
+ unsigned int *hmac)
+{
+ unsigned char version, _hmac;
+ unsigned char *psk;
+ size_t len;
+
+ psk = nvme_import_tls_key_versioned(encoded_key, &version,
+ &_hmac, &len);
+ if (!psk)
+ return NULL;
+
+ *hmac = _hmac;
+ *key_len = len;
+ return psk;
}
diff --git a/src/nvme/linux.h b/src/nvme/linux.h
index 8e5e8ad..5dbc092 100644
--- a/src/nvme/linux.h
+++ b/src/nvme/linux.h
@@ -437,6 +437,25 @@ long nvme_revoke_tls_key(const char *keyring, const char *key_type,
char *nvme_export_tls_key(const unsigned char *key_data, int key_len);
/**
+ * nvme_export_tls_key_versioned() - Export a TLS pre-shared key
+ * @version: Indicated the representation of the TLS PSK
+ * @hmac: HMAC algorithm used to transfor the configured PSK
+ * in a retained PSK
+ * @key_data: Raw data of the key
+ * @key_len: Length of @key_data
+ *
+ * Returns @key_data in the PSK Interchange format as defined in section
+ * 3.6.1.5 of the NVMe TCP Transport specification.
+ *
+ * Return: The string containing the TLS identity or NULL with errno set
+ * on error. It is the responsibility of the caller to free the returned
+ * string.
+ */
+char *nvme_export_tls_key_versioned(unsigned char version, unsigned char hmac,
+ const unsigned char *key_data,
+ size_t key_len);
+
+/**
* nvme_import_tls_key() - Import a TLS key
* @encoded_key: TLS key in PSK interchange format
* @key_len: Length of the resulting key data
@@ -452,6 +471,24 @@ unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len,
unsigned int *hmac);
/**
+ * nvme_import_tls_key_versioned() - Import a TLS key
+ * @encoded_key: TLS key in PSK interchange format
+ * @version: Indicated the representation of the TLS PSK
+ * @hmac: HMAC algorithm used to transfor the configured
+ * PSK in a retained PSK
+ * @key_len: Length of the resulting key data
+ *
+ * Imports @key_data in the PSK Interchange format as defined in section
+ * 3.6.1.5 of the NVMe TCP Transport specification.
+ *
+ * Return: The raw data of the PSK or NULL with errno set on error. It is
+ * the responsibility of the caller to free the returned string.
+ */
+unsigned char *nvme_import_tls_key_versioned(const char *encoded_key,
+ unsigned char *version,
+ unsigned char *hmac,
+ size_t *key_len);
+/**
* nvme_submit_passthru - Low level ioctl wrapper for passthru commands
* @fd: File descriptor of the nvme device
* @ioctl_cmd: IOCTL command id
diff --git a/src/nvme/mi.c b/src/nvme/mi.c
index d98c74a..e9f39a8 100644
--- a/src/nvme/mi.c
+++ b/src/nvme/mi.c
@@ -529,6 +529,31 @@ static void nvme_mi_admin_init_resp(struct nvme_mi_resp *resp,
resp->hdr_len = sizeof(*hdr);
}
+static void nvme_mi_control_init_req(struct nvme_mi_req *req,
+ struct nvme_mi_control_req *control_req,
+ __u8 opcode, __u16 cpsp)
+{
+ memset(req, 0, sizeof(*req));
+ memset(control_req, 0, sizeof(*control_req));
+
+ control_req->hdr.type = NVME_MI_MSGTYPE_NVME;
+ control_req->hdr.nmp = (NVME_MI_ROR_REQ << 7) |
+ (NVME_MI_MT_CONTROL << 3); /* we always use command slot 0 */
+ control_req->opcode = opcode;
+ control_req->cpsp = cpu_to_le16(cpsp);
+
+ req->hdr = &control_req->hdr;
+ req->hdr_len = sizeof(*control_req);
+}
+
+static void nvme_mi_control_init_resp(struct nvme_mi_resp *resp,
+ struct nvme_mi_control_resp *control_resp)
+{
+ memset(resp, 0, sizeof(*resp));
+ resp->hdr = &control_resp->hdr;
+ resp->hdr_len = sizeof(*control_resp);
+}
+
static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result)
{
struct nvme_mi_admin_resp_hdr *admin_hdr;
@@ -580,6 +605,26 @@ static int nvme_mi_admin_parse_status(struct nvme_mi_resp *resp, __u32 *result)
return nvme_status;
}
+static int nvme_mi_control_parse_status(struct nvme_mi_resp *resp, __u16 *cpsr)
+{
+ struct nvme_mi_control_resp *control_resp;
+
+ if (resp->hdr_len < sizeof(*control_resp)) {
+ errno = -EPROTO;
+ return -1;
+ }
+ control_resp = (struct nvme_mi_control_resp *)resp->hdr;
+
+ if (control_resp->status)
+ return control_resp->status |
+ (NVME_STATUS_TYPE_MI << NVME_STATUS_TYPE_SHIFT);
+
+ if (cpsr)
+ *cpsr = le16_to_cpu(control_resp->cpsr);
+
+ return control_resp->status;
+}
+
int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
struct nvme_mi_admin_req_hdr *admin_req,
size_t req_data_size,
@@ -589,6 +634,7 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
{
struct nvme_mi_resp resp;
struct nvme_mi_req req;
+ __u32 dlen, doff;
int rc;
/* length/offset checks. The common _submit() API will do further
@@ -647,8 +693,17 @@ int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
/* limit the response size, specify offset */
admin_req->flags = 0x3;
- admin_req->dlen = cpu_to_le32(resp.data_len & 0xffffffff);
- admin_req->doff = cpu_to_le32(resp_data_offset & 0xffffffff);
+
+ /* dlen and doff have different interpretations depending on the data direction */
+ if (req_data_size) {
+ dlen = req_data_size & 0xffffffff;
+ doff = 0;
+ } else {
+ dlen = *resp_data_size & 0xffffffff;
+ doff = resp_data_offset & 0xffffffff;
+ }
+ admin_req->dlen = cpu_to_le32(dlen);
+ admin_req->doff = cpu_to_le32(doff);
rc = nvme_mi_submit(ctrl->ep, &req, &resp);
if (rc)
@@ -812,6 +867,29 @@ int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
return 0;
}
+int nvme_mi_control(nvme_mi_ep_t ep, __u8 opcode,
+ __u16 cpsp, __u16 *result_cpsr)
+{
+ struct nvme_mi_control_resp control_resp;
+ struct nvme_mi_control_req control_req;
+ struct nvme_mi_resp resp;
+ struct nvme_mi_req req;
+ int rc = 0;
+
+ nvme_mi_control_init_req(&req, &control_req, opcode, cpsp);
+ nvme_mi_control_init_resp(&resp, &control_resp);
+
+ rc = nvme_mi_submit(ep, &req, &resp);
+ if (rc)
+ return rc;
+
+ rc = nvme_mi_control_parse_status(&resp, result_cpsr);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
/* retrieves a MCTP-messsage-sized chunk of log page data. offset and len are
* specified within the args->data area. The `offset` parameter is a relative
* offset to the args->lpo !
diff --git a/src/nvme/mi.h b/src/nvme/mi.h
index ae32a90..3ea2b22 100644
--- a/src/nvme/mi.h
+++ b/src/nvme/mi.h
@@ -368,6 +368,52 @@ struct nvme_mi_admin_resp_hdr {
} __attribute__((packed));
/**
+ * enum nvme_mi_control_opcode - Operation code for Control Primitives.
+ * @nvme_mi_control_opcode_pause: Suspend response transmission/timeout
+ * @nvme_mi_control_opcode_resume: Resume from a paused condition
+ * @nvme_mi_control_opcode_abort: Re-initialize a Command Slot to the Idle state
+ * @nvme_mi_control_opcode_get_state: Get the state of a Command Slot
+ * @nvme_mi_control_opcode_replay: Retransmit the Response Message
+ */
+enum nvme_mi_control_opcode {
+ nvme_mi_control_opcode_pause = 0x00,
+ nvme_mi_control_opcode_resume = 0x01,
+ nvme_mi_control_opcode_abort = 0x02,
+ nvme_mi_control_opcode_get_state = 0x03,
+ nvme_mi_control_opcode_replay = 0x04,
+};
+
+/**
+ * struct nvme_mi_control_req - The Control Primitive request.
+ * @hdr: Generic MI message header
+ * @opcode: Control Primitive Opcodes (using &enum nvme_mi_control_opcode)
+ * @tag: flag - Opaque value passed from request to response
+ * @cpsp: Control Primitive Specific Parameter
+ *
+ */
+struct nvme_mi_control_req {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 opcode;
+ __u8 tag;
+ __le16 cpsp;
+} __attribute((packed));
+
+/** struct nvme_mi_control_resp - The Control Primitive response.
+ * @hdr: Generic MI message header
+ * @status: Generic response code, non-zero on failure
+ * @tag: flag - Opaque value passed from request to response
+ * @cpsr: Control Primitive Specific Response
+ *
+ */
+
+struct nvme_mi_control_resp {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 tag;
+ __le16 cpsr;
+} __attribute((packed));
+
+/**
* nvme_mi_status_to_string() - return a string representation of the MI
* status.
* @status: MI response status
@@ -1076,6 +1122,24 @@ static inline int nvme_mi_admin_identify(nvme_mi_ctrl_t ctrl,
}
/**
+ * nvme_mi_control() - Perform a Control Primitive command
+ * @ep: endpoint for MI communication
+ * @opcode: Control Primitive opcode (using &enum nvme_mi_control_opcode)
+ * @cpsp: Control Primitive Specific Parameter
+ * @result_cpsr: Optional field to return the result from the CPSR field
+ *
+ * Perform a Control Primitive command, using the opcode specified in @opcode
+ * Stores the result from the CPSR field in @result_cpsr if set.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &enum nvme_mi_control_opcode
+ *
+ */
+int nvme_mi_control(nvme_mi_ep_t ep, __u8 opcode,
+ __u16 cpsp, __u16 *result_cpsr);
+
+/**
* nvme_mi_admin_identify_cns_nsid() - Perform an Admin identify command using
* specific CNS/NSID parameters.
* @ctrl: Controller to process identify command
@@ -2369,6 +2433,36 @@ static inline int nvme_mi_admin_get_log_persistent_event(nvme_mi_ctrl_t ctrl,
}
/**
+ * nvme_mi_admin_get_log_lockdown() - Retrieve lockdown Log
+ * @ctrl: Controller to query
+ * @cnscp: Contents and Scope of Command and Feature Identifier Lists
+ * @lockdown_log: Buffer to store the lockdown log
+ *
+ * Return: The nvme command status if a response was received (see
+ * &enum nvme_status_field) or -1 with errno set otherwise.
+ */
+static inline int nvme_mi_admin_get_log_lockdown(nvme_mi_ctrl_t ctrl,
+ __u8 cnscp, struct nvme_lockdown_log *lockdown_log)
+{
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = lockdown_log,
+ .args_size = sizeof(args),
+ .lid = NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN,
+ .len = sizeof(*lockdown_log),
+ .nsid = NVME_NSID_ALL,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = cnscp,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ return nvme_mi_admin_get_log(ctrl, &args);
+}
+
+/**
* nvme_mi_admin_security_send() - Perform a Security Send command on a
* controller.
* @ctrl: Controller to send command to
diff --git a/src/nvme/private.h b/src/nvme/private.h
index 3fa5aca..48ddedc 100644
--- a/src/nvme/private.h
+++ b/src/nvme/private.h
@@ -85,6 +85,9 @@ struct nvme_ctrl {
char *trsvcid;
char *dhchap_key;
char *dhchap_ctrl_key;
+ char *keyring;
+ char *tls_key_identity;
+ char *tls_key;
char *cntrltype;
char *cntlid;
char *dctype;
@@ -297,4 +300,7 @@ void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops);
#define SECTOR_SIZE 512
#define SECTOR_SHIFT 9
+int __nvme_import_keys_from_config(nvme_host_t h, nvme_ctrl_t c,
+ long *keyring_id, long *key_id);
+
#endif /* _LIBNVME_PRIVATE_H */
diff --git a/src/nvme/tree.c b/src/nvme/tree.c
index 3722461..7fc2013 100644
--- a/src/nvme/tree.c
+++ b/src/nvme/tree.c
@@ -1151,6 +1151,51 @@ void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key)
c->dhchap_ctrl_key = strdup(key);
}
+const char *nvme_ctrl_get_keyring(nvme_ctrl_t c)
+{
+ return c->keyring;
+}
+
+void nvme_ctrl_set_keyring(nvme_ctrl_t c, const char *keyring)
+{
+ if (c->keyring) {
+ free(c->keyring);
+ c->keyring = NULL;
+ }
+ if (keyring)
+ c->keyring = strdup(keyring);
+}
+
+const char *nvme_ctrl_get_tls_key_identity(nvme_ctrl_t c)
+{
+ return c->tls_key_identity;
+}
+
+void nvme_ctrl_set_tls_key_identity(nvme_ctrl_t c, const char *identity)
+{
+ if (c->tls_key_identity) {
+ free(c->tls_key_identity);
+ c->tls_key_identity = NULL;
+ }
+ if (identity)
+ c->tls_key_identity = strdup(identity);
+}
+
+const char *nvme_ctrl_get_tls_key(nvme_ctrl_t c)
+{
+ return c->tls_key;
+}
+
+void nvme_ctrl_set_tls_key(nvme_ctrl_t c, const char *key)
+{
+ if (c->tls_key) {
+ free(c->tls_key);
+ c->tls_key = NULL;
+ }
+ if (key)
+ c->tls_key = strdup(key);
+}
+
void nvme_ctrl_set_discovered(nvme_ctrl_t c, bool discovered)
{
c->discovered = discovered;
@@ -1232,6 +1277,9 @@ void nvme_deconfigure_ctrl(nvme_ctrl_t c)
FREE_CTRL_ATTR(c->sqsize);
FREE_CTRL_ATTR(c->dhchap_key);
FREE_CTRL_ATTR(c->dhchap_ctrl_key);
+ FREE_CTRL_ATTR(c->keyring);
+ FREE_CTRL_ATTR(c->tls_key_identity);
+ FREE_CTRL_ATTR(c->tls_key);
FREE_CTRL_ATTR(c->address);
FREE_CTRL_ATTR(c->dctype);
FREE_CTRL_ATTR(c->cntrltype);
@@ -1883,11 +1931,73 @@ static char *nvme_ctrl_lookup_phy_slot(nvme_root_t r, const char *address)
return NULL;
}
+static void nvme_read_sysfs_dhchap(nvme_root_t r, nvme_ctrl_t c)
+{
+ char *host_key, *ctrl_key;
+
+ host_key = nvme_get_ctrl_attr(c, "dhchap_secret");
+ if (host_key && c->s && c->s->h && c->s->h->dhchap_key &&
+ (!strcmp(c->s->h->dhchap_key, host_key) ||
+ !strcmp("none", host_key))) {
+ free(host_key);
+ host_key = NULL;
+ }
+ if (host_key) {
+ nvme_ctrl_set_dhchap_host_key(c, NULL);
+ c->dhchap_key = host_key;
+ }
+
+ ctrl_key = nvme_get_ctrl_attr(c, "dhchap_ctrl_secret");
+ if (ctrl_key && !strcmp(ctrl_key, "none")) {
+ free(ctrl_key);
+ ctrl_key = NULL;
+ }
+ if (ctrl_key) {
+ nvme_ctrl_set_dhchap_key(c, NULL);
+ c->dhchap_ctrl_key = ctrl_key;
+ }
+}
+
+static void nvme_read_sysfs_tls(nvme_root_t r, nvme_ctrl_t c)
+{
+ char *endptr;
+ long key_id;
+ char *key, *keyring;
+
+ key = nvme_get_ctrl_attr(c, "tls_key");
+ if (!key) {
+ /* tls_key is only present if --tls has been used. */
+ return;
+ }
+ c->cfg.tls = true;
+
+ keyring = nvme_get_ctrl_attr(c, "tls_keyring");
+ nvme_ctrl_set_keyring(c, keyring);
+ free(keyring);
+
+ /* the sysfs entry is not prefixing the id but it's in hex */
+ key_id = strtol(key, &endptr, 16);
+ if (endptr != key)
+ c->cfg.tls_key = key_id;
+
+ free(key);
+
+ key = nvme_get_ctrl_attr(c, "tls_configured_key");
+ if (!key)
+ return;
+
+ /* the sysfs entry is not prefixing the id but it's in hex */
+ key_id = strtol(key, &endptr, 16);
+ if (endptr != key)
+ c->cfg.tls_configured_key = key_id;
+
+ free(key);
+}
+
static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path,
const char *name)
{
DIR *d;
- char *host_key, *tls_psk;
d = opendir(path);
if (!d) {
@@ -1908,34 +2018,12 @@ static int nvme_configure_ctrl(nvme_root_t r, nvme_ctrl_t c, const char *path,
c->queue_count = nvme_get_ctrl_attr(c, "queue_count");
c->serial = nvme_get_ctrl_attr(c, "serial");
c->sqsize = nvme_get_ctrl_attr(c, "sqsize");
- host_key = nvme_get_ctrl_attr(c, "dhchap_secret");
- if (host_key && c->s && c->s->h && c->s->h->dhchap_key &&
- (!strcmp(c->s->h->dhchap_key, host_key) ||
- !strcmp("none", host_key))) {
- free(host_key);
- host_key = NULL;
- }
- if (host_key)
- c->dhchap_key = host_key;
- c->dhchap_ctrl_key = nvme_get_ctrl_attr(c, "dhchap_ctrl_secret");
- if (c->dhchap_ctrl_key && !strcmp(c->dhchap_ctrl_key, "none")) {
- free(c->dhchap_ctrl_key);
- c->dhchap_ctrl_key = NULL;
- }
- tls_psk = nvme_get_ctrl_attr(c, "tls_key");
- if (tls_psk) {
- char *endptr;
- long key_id = strtol(tls_psk, &endptr, 16);
-
- if (endptr != tls_psk) {
- c->cfg.tls_key = key_id;
- c->cfg.tls = true;
- }
- }
c->cntrltype = nvme_get_ctrl_attr(c, "cntrltype");
c->cntlid = nvme_get_ctrl_attr(c, "cntlid");
c->dctype = nvme_get_ctrl_attr(c, "dctype");
c->phy_slot = nvme_ctrl_lookup_phy_slot(r, c->address);
+ nvme_read_sysfs_dhchap(r, c);
+ nvme_read_sysfs_tls(r, c);
errno = 0; /* cleanup after nvme_get_ctrl_attr() */
return 0;
diff --git a/src/nvme/tree.h b/src/nvme/tree.h
index 1b583cd..818e17b 100644
--- a/src/nvme/tree.h
+++ b/src/nvme/tree.h
@@ -1099,6 +1099,51 @@ const char *nvme_ctrl_get_dhchap_key(nvme_ctrl_t c);
void nvme_ctrl_set_dhchap_key(nvme_ctrl_t c, const char *key);
/**
+ * nvme_ctrl_get_keyring() - Return keyring
+ * @c: Controller to be used for the lookup
+ *
+ * Return: Keyring or NULL if not set
+ */
+const char *nvme_ctrl_get_keyring(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_keyring() - Set keyring
+ * @c: Controller for which the keyring should be set
+ * @keyring: Keyring name
+ */
+void nvme_ctrl_set_keyring(nvme_ctrl_t c, const char *keyring);
+
+/**
+ * nvme_ctrl_get_tls_key_identity() - Return Derive TLS Identity
+ * @c: Controller to be used for the lookup
+ *
+ * Return: Derive TLS Identity or NULL if not set
+ */
+const char *nvme_ctrl_get_tls_key_identity(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_tls_key_identity() - Set Derive TLS Identity
+ * @c: Controller for which the key should be set
+ * @identity: Derive TLS identity or NULL to clear existing key
+ */
+void nvme_ctrl_set_tls_key_identity(nvme_ctrl_t c, const char *identity);
+
+/**
+ * nvme_ctrl_get_tls_key() - Return Derive TLS PSK
+ * @c: Controller to be used for the lookup
+ *
+ * Return: Key in PSK interchange format or NULL if not set
+ */
+const char *nvme_ctrl_get_tls_key(nvme_ctrl_t c);
+
+/**
+ * nvme_ctrl_set_tls_key() - Set Derive TLS PSK
+ * @c: Controller for which the key should be set
+ * @key: Key in interchange format or NULL to clear existing key
+ */
+void nvme_ctrl_set_tls_key(nvme_ctrl_t c, const char *key);
+
+/**
* nvme_ctrl_get_config() - Fabrics configuration of a controller
* @c: Controller instance
*
diff --git a/src/nvme/types.h b/src/nvme/types.h
index 5fa969d..7d143d3 100644
--- a/src/nvme/types.h
+++ b/src/nvme/types.h
@@ -46,6 +46,24 @@
(((__u32)(value) & NVME_##name##_MASK) << NVME_##name##_SHIFT)
/**
+ * NVME_CHECK() - check value to compare field value
+ * @value: The value to be checked
+ * @name: The name of the sub-field within an nvme value
+ * @check: The sub-field value to check
+ *
+ * Returns: The result of compare the value and the sub-field value
+ */
+#define NVME_CHECK(value, name, check) ((value) == NVME_##name##_##check)
+
+/**
+ * NVME_VAL() - get mask value shifted
+ * @name: The name of the sub-field within an nvme value
+ *
+ * Returns: The mask value shifted
+ */
+#define NVME_VAL(name) (NVME_##name##_MASK << NVME_##name##_SHIFT)
+
+/**
* enum nvme_constants - A place to stash various constant nvme values
* @NVME_NSID_ALL: A broadcast value that is used to specify all
* namespaces
@@ -1293,7 +1311,8 @@ struct nvme_id_psd {
* @rsvd358: Reserved
* @megcap: Max Endurance Group Capacity indicates the maximum capacity
* of a single Endurance Group.
- * @rsvd384: Reserved
+ * @tmpthha: Temperature Threshold Hysteresis Attributes
+ * @rsvd385: Reserved
* @sqes: Submission Queue Entry Size, see &enum nvme_id_ctrl_sqes.
* @cqes: Completion Queue Entry Size, see &enum nvme_id_ctrl_cqes.
* @maxcmd: Maximum Outstanding Commands indicates the maximum number of
@@ -1341,6 +1360,39 @@ struct nvme_id_psd {
* total number of outstanding I/O commands across all I/O queues
* on the controller for optimal operation.
* @rsvd568: Reserved
+ * @cmmrtd: Controller Maximum Memory Range Tracking Descriptors indicates
+ * the maximum number of Memory Range Tracking Descriptors the
+ * controller supports.
+ * @nmmrtd: NVM Subsystem Maximum Memory Range Tracking Descriptors
+ * indicates the maximum number of Memory Range Tracking Descriptors
+ * the NVM subsystem supports.
+ * @minmrtg: Minimum Memory Range Tracking Granularity indicates the minimum
+ * value supported in the Requested Memory Range Tracking
+ * Granularity (RMRTG) field of the Track Memory Ranges data
+ * structure.
+ * @maxmrtg: Maximum Memory Range Tracking Granularity indicates the maximum
+ * value supported in the Requested Memory Range Tracking
+ * Granularity (RMRTG) field of the Track Memory Ranges data
+ * structure.
+ * @trattr: Tracking Attributes indicates supported attributes for the
+ * Track Send command and Track Receive command.
+ * @rsvd577: Reserved
+ * @mcudmq: Maximum Controller User Data Migration Queues indicates the
+ * maximum number of User Data Migration Queues supported by the
+ * controller.
+ * @mnsudmq: Maximum NVM Subsystem User Data Migration Queues indicates the
+ * maximum number of User Data Migration Queues supported by the NVM
+ * subsystem.
+ * @mcmr: Maximum CDQ Memory Ranges indicates the maximum number of
+ * memory ranges allowed to be specified by the PRP1 field of a
+ * Controller Data Queue command.
+ * @nmcmr: NVM Subsystem Maximum CDQ Memory Ranges indicates the maximum
+ * number of memory ranges for all Controller Data Queues in the
+ * NVM subsystem.
+ * @mcdqpc: Maximum Controller Data Queue PRP Count indicates the maximum
+ * number of PRPs allowed to be specified in the PRP list in the
+ * Controller Data Queue command.
+ * @rsvd588: Reserved
* @subnqn: NVM Subsystem NVMe Qualified Name, UTF-8 null terminated string
* @rsvd1024: Reserved
* @ioccsz: I/O Queue Command Capsule Supported Size, defines the maximum
@@ -1426,7 +1478,8 @@ struct nvme_id_ctrl {
__le16 domainid;
__u8 rsvd358[10];
__u8 megcap[16];
- __u8 rsvd384[128];
+ __u8 tmpthha;
+ __u8 rsvd385[127];
__u8 sqes;
__u8 cqes;
__le16 maxcmd;
@@ -1446,7 +1499,19 @@ struct nvme_id_ctrl {
__u8 maxdna[16];
__le32 maxcna;
__le32 oaqd;
- __u8 rsvd568[200];
+ __u8 rsvd568[2];
+ __u16 cmmrtd;
+ __u16 nmmrtd;
+ __u8 minmrtg;
+ __u8 maxmrtg;
+ __u8 trattr;
+ __u8 rsvd577;
+ __u16 mcudmq;
+ __u16 mnsudmq;
+ __u16 mcmr;
+ __u16 nmcmr;
+ __u16 mcdqpc;
+ __u8 rsvd588[180];
char subnqn[NVME_NQN_LENGTH];
__u8 rsvd1024[768];
@@ -1526,29 +1591,103 @@ enum nvme_id_ctrl_cmic {
/**
* enum nvme_id_ctrl_oaes - Optional Asynchronous Events Supported
+ * @NVME_CTRL_OAES_NA_SHIFT: Shift amount to get the Namespace Attribute Notices event supported
+ * @NVME_CTRL_OAES_FA_SHIFT: Shift amount to get the Firmware Activation Notices event supported
+ * @NVME_CTRL_OAES_ANA_SHIFT: Shift amount to get the ANA Change Notices supported
+ * @NVME_CTRL_OAES_PLEA_SHIFT: Shift amount to get the Predictable Latency Event Aggregate Log
+ * Change Notices event supported
+ * @NVME_CTRL_OAES_LBAS_SHIFT: Shift amount to get the LBA Status Information Notices event
+ * supported
+ * @NVME_CTRL_OAES_EGE_SHIFT: Shift amount to get the Endurance Group Events Aggregate Log Change
+ * Notices event supported
+ * @NVME_CTRL_OAES_NS_SHIFT: Shift amount to get the Normal NVM Subsystem Shutdown event supported
+ * @NVME_CTRL_OAES_TTH_SHIFT: Shift amount to get the Temperature Threshold Hysteresis Recovery
+ * event supported
+ * @NVME_CTRL_OAES_RGCNS_SHIFT: Shift amount to get the Reachability Groups Change Notices supported
+ * @NVME_CTRL_OAES_ANSAN_SHIFT: Shift amount to get the Allocated Namespace Attribute Notices
+ * supported
+ * @NVME_CTRL_OAES_ZD_SHIFT: Shift amount to get the Zone Descriptor Change Notifications supported
+ * @NVME_CTRL_OAES_DL_SHIFT: Shift amount to get the Discover Log Page Change Notifications
+ * supported
+ * @NVME_CTRL_OAES_NA_MASK: Mask to get the Namespace Attribute Notices event supported
+ * @NVME_CTRL_OAES_FA_MASK: Mask to get the Firmware Activation Notices event supported
+ * @NVME_CTRL_OAES_ANA_MASK: Mask to get the ANA Change Notices supported
+ * @NVME_CTRL_OAES_PLEA_MASK: Mask to get the Predictable Latency Event Aggregate Log Change Notices
+ * event supported
+ * @NVME_CTRL_OAES_LBAS_MASK: Mask to get the LBA Status Information Notices event supported
+ * @NVME_CTRL_OAES_EGE_MASK: Mask to get the Endurance Group Events Aggregate Log Change Notices
+ * event supported
+ * @NVME_CTRL_OAES_NS_MASK: Mask to get the Normal NVM Subsystem Shutdown event supported
+ * @NVME_CTRL_OAES_TTH_MASK: Mask to get the Temperature Threshold Hysteresis Recovery event
+ * supported
+ * @NVME_CTRL_OAES_RGCNS_MASK: Mask to get the Reachability Groups Change Notices supported
+ * @NVME_CTRL_OAES_ANSAN_MASK: Mask to get the Allocated Namespace Attribute Notices supported
+ * @NVME_CTRL_OAES_ZD_MASK: Mask to get the Zone Descriptor Change Notifications supported
+ * @NVME_CTRL_OAES_DL_MASK: Mask to get the Discover Log Page Change Notifications supported
* @NVME_CTRL_OAES_NA: Namespace Attribute Notices event supported
* @NVME_CTRL_OAES_FA: Firmware Activation Notices event supported
* @NVME_CTRL_OAES_ANA: ANA Change Notices supported
- * @NVME_CTRL_OAES_PLEA: Predictable Latency Event Aggregate Log
- * Change Notices event supported
+ * @NVME_CTRL_OAES_PLEA: Predictable Latency Event Aggregate Log Change Notices event supported
* @NVME_CTRL_OAES_LBAS: LBA Status Information Notices event supported
- * @NVME_CTRL_OAES_EGE: Endurance Group Events Aggregate Log Change
- * Notices event supported
+ * @NVME_CTRL_OAES_EGE: Endurance Group Events Aggregate Log Change Notices event supported
* @NVME_CTRL_OAES_NS: Normal NVM Subsystem Shutdown event supported
+ * @NVME_CTRL_OAES_TTH: Temperature Threshold Hysteresis Recovery event supported
+ * @NVME_CTRL_OAES_RGCNS: Reachability Groups Change Notices supported
+ * @NVME_CTRL_OAES_ANSAN: Allocated Namespace Attribute Notices supported
* @NVME_CTRL_OAES_ZD: Zone Descriptor Change Notifications supported
* @NVME_CTRL_OAES_DL: Discover Log Page Change Notifications supported
*/
enum nvme_id_ctrl_oaes {
- NVME_CTRL_OAES_NA = 1 << 8,
- NVME_CTRL_OAES_FA = 1 << 9,
- NVME_CTRL_OAES_ANA = 1 << 11,
- NVME_CTRL_OAES_PLEA = 1 << 12,
- NVME_CTRL_OAES_LBAS = 1 << 13,
- NVME_CTRL_OAES_EGE = 1 << 14,
- NVME_CTRL_OAES_NS = 1 << 15,
- NVME_CTRL_OAES_ZD = 1 << 27,
- NVME_CTRL_OAES_DL = 1 << 31,
-};
+ NVME_CTRL_OAES_NA_SHIFT = 8,
+ NVME_CTRL_OAES_FA_SHIFT = 9,
+ NVME_CTRL_OAES_ANA_SHIFT = 11,
+ NVME_CTRL_OAES_PLEA_SHIFT = 12,
+ NVME_CTRL_OAES_LBAS_SHIFT = 13,
+ NVME_CTRL_OAES_EGE_SHIFT = 14,
+ NVME_CTRL_OAES_NS_SHIFT = 15,
+ NVME_CTRL_OAES_TTH_SHIFT = 16,
+ NVME_CTRL_OAES_RGCNS_SHIFT = 17,
+ NVME_CTRL_OAES_ANSAN_SHIFT = 19,
+ NVME_CTRL_OAES_ZD_SHIFT = 27,
+ NVME_CTRL_OAES_DL_SHIFT = 31,
+ NVME_CTRL_OAES_NA_MASK = 0x1,
+ NVME_CTRL_OAES_FA_MASK = 0x1,
+ NVME_CTRL_OAES_ANA_MASK = 0x1,
+ NVME_CTRL_OAES_PLEA_MASK = 0x1,
+ NVME_CTRL_OAES_LBAS_MASK = 0x1,
+ NVME_CTRL_OAES_EGE_MASK = 0x1,
+ NVME_CTRL_OAES_NS_MASK = 0x1,
+ NVME_CTRL_OAES_TTH_MASK = 0x1,
+ NVME_CTRL_OAES_RGCNS_MASK = 0x1,
+ NVME_CTRL_OAES_ANSAN_MASK = 0x1,
+ NVME_CTRL_OAES_ZD_MASK = 0x1,
+ NVME_CTRL_OAES_DL_MASK = 0x1,
+ NVME_CTRL_OAES_NA = NVME_VAL(CTRL_OAES_NA),
+ NVME_CTRL_OAES_FA = NVME_VAL(CTRL_OAES_FA),
+ NVME_CTRL_OAES_ANA = NVME_VAL(CTRL_OAES_ANA),
+ NVME_CTRL_OAES_PLEA = NVME_VAL(CTRL_OAES_PLEA),
+ NVME_CTRL_OAES_LBAS = NVME_VAL(CTRL_OAES_LBAS),
+ NVME_CTRL_OAES_EGE = NVME_VAL(CTRL_OAES_EGE),
+ NVME_CTRL_OAES_NS = NVME_VAL(CTRL_OAES_NS),
+ NVME_CTRL_OAES_TTH = NVME_VAL(CTRL_OAES_TTH),
+ NVME_CTRL_OAES_RGCNS = NVME_VAL(CTRL_OAES_RGCNS),
+ NVME_CTRL_OAES_ANSAN = NVME_VAL(CTRL_OAES_ANSAN),
+ NVME_CTRL_OAES_ZD = NVME_VAL(CTRL_OAES_ZD),
+ NVME_CTRL_OAES_DL = NVME_VAL(CTRL_OAES_DL),
+};
+
+#define NVME_CTRL_OAES_NAN(oaes) NVME_GET(oaes, CTRL_OAES_NA)
+#define NVME_CTRL_OAES_FAN(oaes) NVME_GET(oaes, CTRL_OAES_FA)
+#define NVME_CTRL_OAES_ANACN(oaes) NVME_GET(oaes, CTRL_OAES_ANA)
+#define NVME_CTRL_OAES_PLEALCN(oaes) NVME_GET(oaes, CTRL_OAES_PLEA)
+#define NVME_CTRL_OAES_LBASIAN(oaes) NVME_GET(oaes, CTRL_OAES_LBAS)
+#define NVME_CTRL_OAES_EGEALPCN(oaes) NVME_GET(oaes, CTRL_OAES_EGE)
+#define NVME_CTRL_OAES_NNVMSS(oaes) NVME_GET(oaes, CTRL_OAES_NS)
+#define NVME_CTRL_OAES_TTHR(oaes) NVME_GET(oaes, CTRL_OAES_TTH)
+#define NVME_CTRL_OAES_RGCNS(oaes) NVME_GET(oaes, CTRL_OAES_RGCNS)
+#define NVME_CTRL_OAES_ANSAN(oaes) NVME_GET(oaes, CTRL_OAES_ANSAN)
+#define NVME_CTRL_OAES_ZDCN(oaes) NVME_GET(oaes, CTRL_OAES_ZD)
+#define NVME_CTRL_OAES_DLPCN(oaes) NVME_GET(oaes, CTRL_OAES_DL)
/**
* enum nvme_id_ctrl_ctratt - Controller attributes
@@ -1972,6 +2111,14 @@ enum nvme_id_ctrl_fuses {
/**
* enum nvme_id_ctrl_fna - This field indicates attributes for the Format NVM
* command.
+ * @NVME_CTRL_FNA_FMT_ALL_NS_SHIFT: Shift amount to get the format applied to all namespaces
+ * @NVME_CTRL_FNA_SEC_ALL_NS_SHIFT: Shift amount to get the secure erase applied to all namespaces
+ * @NVME_CTRL_FNA_CES_SHIFT: Shift amount to get the cryptographic erase supported
+ * @NVME_CTRL_FNA_NSID_ALL_F_SHIFT: Shift amount to get the format supported an NSID FFFFFFFFh
+ * @NVME_CTRL_FNA_FMT_ALL_NS_MASK: Mask to get the format applied to all namespaces
+ * @NVME_CTRL_FNA_SEC_ALL_NS_MASK: Mask to get the secure erase applied to all namespaces
+ * @NVME_CTRL_FNA_CES_MASK: Mask to get the cryptographic erase supported
+ * @NVME_CTRL_FNA_NSID_ALL_F_MASK: Mask to get the format supported an NSID FFFFFFFFh
* @NVME_CTRL_FNA_FMT_ALL_NAMESPACES: If set, then all namespaces in an NVM
* subsystem shall be configured with the
* same attributes and a format (excluding
@@ -1996,11 +2143,24 @@ enum nvme_id_ctrl_fuses {
* FFFFFFFFh.
*/
enum nvme_id_ctrl_fna {
- NVME_CTRL_FNA_FMT_ALL_NAMESPACES = 1 << 0,
- NVME_CTRL_FNA_SEC_ALL_NAMESPACES = 1 << 1,
- NVME_CTRL_FNA_CRYPTO_ERASE = 1 << 2,
- NVME_CTRL_FNA_NSID_FFFFFFFF = 1 << 3,
-};
+ NVME_CTRL_FNA_FMT_ALL_NS_SHIFT = 0,
+ NVME_CTRL_FNA_SEC_ALL_NS_SHIFT = 1,
+ NVME_CTRL_FNA_CES_SHIFT = 2,
+ NVME_CTRL_FNA_NSID_ALL_F_SHIFT = 3,
+ NVME_CTRL_FNA_FMT_ALL_NS_MASK = 0x1,
+ NVME_CTRL_FNA_SEC_ALL_NS_MASK = 0x1,
+ NVME_CTRL_FNA_CES_MASK = 0x1,
+ NVME_CTRL_FNA_NSID_ALL_F_MASK = 0x1,
+ NVME_CTRL_FNA_FMT_ALL_NAMESPACES = NVME_VAL(CTRL_FNA_FMT_ALL_NS),
+ NVME_CTRL_FNA_SEC_ALL_NAMESPACES = NVME_VAL(CTRL_FNA_SEC_ALL_NS),
+ NVME_CTRL_FNA_CRYPTO_ERASE = NVME_VAL(CTRL_FNA_CES),
+ NVME_CTRL_FNA_NSID_FFFFFFFF = NVME_VAL(CTRL_FNA_NSID_ALL_F),
+};
+
+#define NVME_CTRL_FNA_FMT_ALL_NS(fna) NVME_GET(fna, CTRL_FNA_FMT_ALL_NS)
+#define NVME_CTRL_FNA_SEC_ALL_NS(fna) NVME_GET(fna, CTRL_FNA_SEC_ALL_NS)
+#define NVME_CTRL_FNA_CES(fna) NVME_GET(fna, CTRL_FNA_CES)
+#define NVME_CTRL_FNA_NSID_ALL_F(fna) NVME_GET(fna, CTRL_FNA_NSID_ALL_F)
/**
* enum nvme_id_ctrl_vwc - Volatile write cache
@@ -2710,7 +2870,9 @@ struct nvme_ns_list {
* @dmsl: Dataset Management Size Limit
* @rsvd16: Reserved
* @aocs: Admin Optional Command Support
- * @rsvd20: Reserved
+ * @ver: Version
+ * @lbamqf: LBA Migration Queue Format
+ * @rsvd25: Reserved
*/
struct nvme_id_ctrl_nvm {
__u8 vsl;
@@ -2721,7 +2883,9 @@ struct nvme_id_ctrl_nvm {
__le64 dmsl;
__u8 rsvd16[2];
__le16 aocs;
- __u8 rsvd20[4076];
+ __le32 ver;
+ __u8 lbamqf;
+ __u8 rsvd25[4071];
};
/**
@@ -2732,7 +2896,11 @@ struct nvme_id_ctrl_nvm {
* @rsvd10: Reserved
* @elbaf: List of Extended LBA Format Support
* @npdgl: Namespace Preferred Deallocate Granularity Large
- * @rsvd272: Reserved
+ * @nprg: Namespace Preferred Read Granularity
+ * @npra: Namespace Preferred Read Alignment
+ * @nors: Namespace Optimal Read Size
+ * @npdal: Namespace Preferred Deallocate Alignment Large
+ * @lbapss: LBA Format Placement Shard Size
* @tlbaag: Tracked LBA Allocation Granularity
* @rsvd296: Reserved
*/
@@ -2743,7 +2911,11 @@ struct nvme_nvm_id_ns {
__u8 rsvd10[2];
__le32 elbaf[64];
__le32 npdgl;
- __u8 rsvd272[20];
+ __le32 nprg;
+ __le32 npra;
+ __le32 nors;
+ __le32 npdal;
+ __le32 lbapss;
__le32 tlbaag;
__u8 rsvd296[3800];
};
@@ -3301,6 +3473,7 @@ struct nvme_cmd_effects_log {
* @NVME_CMD_EFFECTS_NCC: Namespace Capability Change
* @NVME_CMD_EFFECTS_NIC: Namespace Inventory Change
* @NVME_CMD_EFFECTS_CCC: Controller Capability Change
+ * @NVME_CMD_EFFECTS_CSER_MASK: Command Submission and Execution Relaxations
* @NVME_CMD_EFFECTS_CSE_MASK: Command Submission and Execution
* @NVME_CMD_EFFECTS_UUID_SEL: UUID Selection Supported
*/
@@ -3310,7 +3483,8 @@ enum nvme_cmd_effects {
NVME_CMD_EFFECTS_NCC = 1 << 2,
NVME_CMD_EFFECTS_NIC = 1 << 3,
NVME_CMD_EFFECTS_CCC = 1 << 4,
- NVME_CMD_EFFECTS_CSE_MASK = 3 << 16,
+ NVME_CMD_EFFECTS_CSER_MASK = 3 << 14,
+ NVME_CMD_EFFECTS_CSE_MASK = 7 << 16,
NVME_CMD_EFFECTS_UUID_SEL = 1 << 19,
};
@@ -3884,6 +4058,7 @@ struct nvme_persistent_event_entry {
* @NVME_PEL_SET_FEATURE_EVENT: Set Feature Event
* @NVME_PEL_TELEMETRY_CRT: Telemetry Log Create Event
* @NVME_PEL_THERMAL_EXCURSION_EVENT: Thermal Excursion Event
+ * @NVME_PEL_SANITIZE_MEDIA_VERIF_EVENT:Sanitize Media Verification Event
* @NVME_PEL_VENDOR_SPECIFIC_EVENT: Vendor Specific Event
* @NVME_PEL_TCG_DEFINED_EVENT: TCG Defined Event
*/
@@ -3901,6 +4076,7 @@ enum nvme_persistent_event_types {
NVME_PEL_SET_FEATURE_EVENT = 0x0b,
NVME_PEL_TELEMETRY_CRT = 0x0c,
NVME_PEL_THERMAL_EXCURSION_EVENT = 0x0d,
+ NVME_PEL_SANITIZE_MEDIA_VERIF_EVENT = 0x0e,
NVME_PEL_VENDOR_SPECIFIC_EVENT = 0xde,
NVME_PEL_TCG_DEFINED_EVENT = 0xdf,
};
@@ -4608,6 +4784,20 @@ struct nvme_supported_cap_config_list_log {
};
/**
+ * struct nvme_lockdown_log - Command and Feature Lockdown Log
+ * @cfila: Contents of the Command and Feature Identifier List field in the log page.
+ * @rsvd1: Reserved
+ * @lngth: Length of Command and Feature Identifier List field
+ * @cfil: Command and Feature Identifier List
+ */
+struct nvme_lockdown_log {
+ __u8 cfila;
+ __u8 rsvd1[2];
+ __u8 lngth;
+ __u8 cfil[508];
+};
+
+/**
* struct nvme_resv_notification_log - Reservation Notification Log
* @lpc: Log Page Count
* @rnlpt: See &enum nvme_resv_notify_rnlpt.
@@ -4705,7 +4895,15 @@ enum nvme_resv_notify_rnlpt {
* to be completed in the background when the Sanitize command that
* started that operation is completed. A value of FFFFFFFFh indicates
* that no time period is reported.
- * @rsvd32: Reserved
+ * @etpvds: Estimated Time For Post-Verification Deallocation State: indicates the
+ * number of seconds required to deallocate all media allocated for user data
+ * after exiting the Media Verification state (i.e., the time difference between
+ * entering and exiting the Post-Verification Deallocation state), if that state
+ * is entered as part of the sanitize operation. A value of FFFFFFFFh indicates
+ * that no time period is reported.
+ * @ssi: Sanitize State Information: indicate the state of the Sanitize Operation
+ * State Machine.
+ * @rsvd37: Reserved
*/
struct nvme_sanitize_log_page {
__le16 sprog;
@@ -4717,7 +4915,9 @@ struct nvme_sanitize_log_page {
__le32 etond;
__le32 etbend;
__le32 etcend;
- __u8 rsvd32[480];
+ __le32 etpvds;
+ __u8 ssi;
+ __u8 rsvd37[475];
};
/**
@@ -4761,6 +4961,10 @@ struct nvme_sanitize_log_page {
* the NVM subsystem has never been sanitized;
* or since the most recent successful sanitize
* operation.
+ * @NVME_SANITIZE_SSTAT_MVCNCLD_SHIFT: Shift amount to get the value of Media Verification
+ * Canceled bit of Sanitize status field.
+ * @NVME_SANITIZE_SSTAT_MVCNCLD_MASK: Mask to get the value of Media Verification Canceled
+ * bit of Sanitize status field.
*/
enum nvme_sanitize_sstat {
NVME_SANITIZE_SSTAT_STATUS_SHIFT = 0,
@@ -4775,6 +4979,59 @@ enum nvme_sanitize_sstat {
NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT = 8,
NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_MASK = 0x1,
NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED = 1 << NVME_SANITIZE_SSTAT_GLOBAL_DATA_ERASED_SHIFT,
+ NVME_SANITIZE_SSTAT_MVCNCLD_SHIFT = 9,
+ NVME_SANITIZE_SSTAT_MVCNCLD_MASK = 0x1,
+};
+
+/**
+ * enum nvme_sanitize_ssi - Sanitize State Information (SSI)
+ * @NVME_SANITIZE_SSI_SANS_SHIFT: Shift amount to get the value of Sanitize State
+ * from Sanitize State Information (SSI) field.
+ * @NVME_SANITIZE_SSI_SANS_MASK: Mask to get the value of Sanitize State from
+ * Sanitize State Information (SSI) field.
+ * @NVME_SANITIZE_SSI_FAILS_SHIFT: Shift amount to get the value of Failure State
+ * from Sanitize State Information (SSI) field.
+ * @NVME_SANITIZE_SSI_FAILS_MASK: Mask to get the value of Failure State from
+ * Sanitize State Information (SSI) field.
+ * @NVME_SANITIZE_SSI_IDLE: No sanitize operation is in process.
+ * @NVME_SANITIZE_SSI_RESTRICT_PROCESSING: The Sanitize operation is in Restricted Processing
+ * State.
+ * @NVME_SANITIZE_SSI_RESTRICT_FAILURE: The Sanitize operation is in Restricted Failure
+ * State. This state is entered if sanitize processing
+ * was performed in the Restricted Processing state and
+ * sanitize processing failed or a failure occurred
+ * during deallocation of media allocated for user data
+ * in the Post-Verification Deallocation state.
+ * @NVME_SANITIZE_SSI_UNRESTRICT_PROCESSING: The Sanitize operation is in Unrestricted Processing
+ * State.
+ * @NVME_SANITIZE_SSI_UNRESTRICT_FAILURE: The Sanitize operation is in Unrestricted Failure
+ * State. This state is entered if sanitize processing
+ * was performed in the Unrestricted Processing state
+ * and sanitize processing failed or a failure occurred
+ * during deallocation of media allocated for user data
+ * in the Post-Verification.
+ * @NVME_SANITIZE_SSI_MEDIA_VERIFICATION: The Sanitize operation is in Media Verification
+ * State. In this state, the sanitize processing
+ * completed successfully, and all media allocated for
+ * user data in the sanitization target is readable by
+ * the host for purposes of verifying sanitization.
+ * @NVME_SANITIZE_SSI_POST_VERIF_DEALLOC: The Sanitize operation is in Post-Verification
+ * Deallocation State. In this state, the controller
+ * shall deallocate all media allocated for user data
+ * in the sanitization target.
+ */
+enum nvme_sanitize_ssi {
+ NVME_SANITIZE_SSI_SANS_SHIFT = 0,
+ NVME_SANITIZE_SSI_SANS_MASK = 0xf,
+ NVME_SANITIZE_SSI_FAILS_SHIFT = 4,
+ NVME_SANITIZE_SSI_FAILS_MASK = 0xf,
+ NVME_SANITIZE_SSI_IDLE = 0,
+ NVME_SANITIZE_SSI_RESTRICT_PROCESSING = 1,
+ NVME_SANITIZE_SSI_RESTRICT_FAILURE = 2,
+ NVME_SANITIZE_SSI_UNRESTRICT_PROCESSING = 3,
+ NVME_SANITIZE_SSI_UNRESTRICT_FAILURE = 4,
+ NVME_SANITIZE_SSI_MEDIA_VERIFICATION = 5,
+ NVME_SANITIZE_SSI_POST_VERIF_DEALLOC = 6,
};
/**
@@ -4908,6 +5165,46 @@ enum nvme_fdp_config_fdpa {
};
/**
+ * enum nvme_lockdown_log_scope - lockdown log page scope attributes
+ * @NVME_LOCKDOWN_ADMIN_CMD: Scope value for Admin commandS
+ * @NVME_LOCKDOWN_FEATURE_ID: Scope value for Feature ID
+ * @NVME_LOCKDOWN_MI_CMD_SET: Scope value for Management Interface commands
+ * @NVME_LOCKDOWN_PCI_CMD_SET: Scope value for PCI commands
+ */
+enum nvme_lockdown_log_scope {
+ NVME_LOCKDOWN_ADMIN_CMD = 0x0,
+ NVME_LOCKDOWN_FEATURE_ID = 0x2,
+ NVME_LOCKDOWN_MI_CMD_SET = 0x3,
+ NVME_LOCKDOWN_PCI_CMD_SET = 0x4,
+};
+
+/**
+ * enum nvme_lockdown_log_contents - lockdown log page content attributes
+ * @NVME_LOCKDOWN_SUPPORTED_CMD: Content value for Supported commands
+ * @NVME_LOCKDOWN_PROHIBITED_CMD: Content value for prohibited commands
+ * @NVME_LOCKDOWN_PROHIBITED_OUTOFBAND_CMD: Content value for prohibited side band commands
+ */
+enum nvme_lockdown_log_contents {
+ NVME_LOCKDOWN_SUPPORTED_CMD = 0x0,
+ NVME_LOCKDOWN_PROHIBITED_CMD = 0x1,
+ NVME_LOCKDOWN_PROHIBITED_OUTOFBAND_CMD = 0x2,
+};
+
+/**
+ * enum nvme_lockdown_scope_contents - Lockdown Log shift and mask
+ * @NVME_LOCKDOWN_SS_SHIFT: Lockdown log scope select Shift
+ * @NVME_LOCKDOWN_SS_MASK: Lockdown log scope select Mask
+ * @NVME_LOCKDOWN_CS_SHIFT: Lockdown log contents Shift
+ * @NVME_LOCKDOWN_CS_MASK: Lockdown log contents Mask
+ */
+enum nvme_lockdown_scope_contents {
+ NVME_LOCKDOWN_SS_SHIFT = 0,
+ NVME_LOCKDOWN_SS_MASK = 0xf,
+ NVME_LOCKDOWN_CS_SHIFT = 4,
+ NVME_LOCKDOWN_CS_MASK = 0x3,
+};
+
+/**
* struct nvme_fdp_config_desc - FDP Configuration Descriptor
* @size: Descriptor size
* @fdpa: FDP Attributes (&enum nvme_fdp_config_fdpa)
@@ -5189,6 +5486,22 @@ struct nvme_lba_status {
};
/**
+ * enum nvme_lba_status_cmpc - Get LBA Status Command Completion Condition
+ * @NVME_LBA_STATUS_CMPC_NO_CMPC: No indication of the completion condition
+ * @NVME_LBA_STATUS_CMPC_INCOMPLETE: Command completed, but additional LBA Status
+ * Descriptor Entries are available to transfer
+ * or scan did not complete (if ATYPE = 10h)
+ * @NVME_LBA_STATUS_CMPC_COMPLETE: Completed the specified action over the number
+ * of LBAs specified in the Range Length field and
+ * transferred all available LBA Status Descriptors
+ */
+enum nvme_lba_status_cmpc {
+ NVME_LBA_STATUS_CMPC_NO_CMPC = 0x0,
+ NVME_LBA_STATUS_CMPC_INCOMPLETE = 0x1,
+ NVME_LBA_STATUS_CMPC_COMPLETE = 0x2,
+};
+
+/**
* struct nvme_feat_auto_pst - Autonomous Power State Transition
* @apst_entry: See &enum nvme_apst_entry
*/
@@ -6956,6 +7269,20 @@ struct nvme_mi_vpd_hdr {
* Originator field does not match the
* Host NQN used by the DDC to connect
* to the CDC.
+ * @NVME_SC_INVALID_CONTROLER_DATA_QUEUE: This error indicates that the
+ * specified Controller Data Queue
+ * Identifier is invalid for the controller
+ * processing the command.
+ * @NVME_SC_NOT_ENOUGH_RESOURCES: This error indicates that there is not
+ * enough resources in the controller to
+ * process the command.
+ * @NVME_SC_CONTROLLER_SUSPENDED: The operation requested is not allowed if
+ * the specified controller is suspended.
+ * @NVME_SC_CONTROLLER_NOT_SUSPENDED: The operation requested is not allowed if
+ * the specified controller is not
+ * suspended.
+ * @NVME_SC_CONTROLLER_DATA_QUEUE_FULL: The controller detected that a
+ * Controller Data Queue became full.
* @NVME_SC_BAD_ATTRIBUTES: Conflicting Dataset Management Attributes
* @NVME_SC_INVALID_PI: Invalid Protection Information
* @NVME_SC_READ_ONLY: Attempted Write to Read Only Range
@@ -7214,6 +7541,15 @@ enum nvme_status_field {
NVME_SC_ZONEGRP_ORIGINATOR_INVLD = 0x34,
/*
+ * Command Set Specific - Live Migration
+ */
+ NVME_SC_INVALID_CONTROLER_DATA_QUEUE = 0x37,
+ NVME_SC_NOT_ENOUGH_RESOURCES = 0x38,
+ NVME_SC_CONTROLLER_SUSPENDED = 0x39,
+ NVME_SC_CONTROLLER_NOT_SUSPENDED = 0x3A,
+ NVME_SC_CONTROLLER_DATA_QUEUE_FULL = 0x3B,
+
+ /*
* I/O Command Set Specific - NVM commands:
*/
NVME_SC_BAD_ATTRIBUTES = 0x80,
@@ -7491,6 +7827,9 @@ enum nvme_admin_opcode {
* @NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE: I/O Command Set specific ID Namespace
* Data Structure for Allocated Namespace ID
* @NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE: Base Specification 2.0a section 5.17.2.21
+ * @NVME_IDENTIFY_CNS_SUPPORTED_CTRL_STATE_FORMATS: Supported Controller State Formats
+ * identifying the supported NVMe Controller
+ * State data structures
*/
enum nvme_identify_cns {
NVME_IDENTIFY_CNS_NS = 0x00,
@@ -7517,6 +7856,7 @@ enum nvme_identify_cns {
NVME_IDENTIFY_CNS_CSI_ALLOCATED_NS_LIST = 0x1A,
NVME_IDENTIFY_CNS_CSI_ID_NS_DATA_STRUCTURE = 0x1B,
NVME_IDENTIFY_CNS_COMMAND_SET_STRUCTURE = 0x1C,
+ NVME_IDENTIFY_CNS_SUPPORTED_CTRL_STATE_FORMATS = 0x20,
};
/**
@@ -7543,12 +7883,21 @@ enum nvme_identify_cns {
* @NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS: NVMe-MI Commands Supported and Effects
* @NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN: Command and Feature Lockdown
* @NVME_LOG_LID_BOOT_PARTITION: Boot Partition
+ * @NVME_LOG_LID_ROTATIONAL_MEDIA_INFO: Rotational Media Information
+ * @NVME_LOG_LID_DISPERSED_NS_PARTICIPATING_NSS:Dispersed Namespace Participating NVM Subsystems
+ * @NVME_LOG_LID_MGMT_ADDR_LIST: Management Address List
* @NVME_LOG_LID_PHY_RX_EOM: Physical Interface Receiver Eye Opening Measurement
+ * @NVME_LOG_LID_REACHABILITY_GROUPS: Reachability Groups
+ * @NVME_LOG_LID_REACHABILITY_ASSOCIATIONS: Reachability Associations
+ * @NVME_LOG_LID_CHANGED_ALLOC_NS_LIST: Changed Allocated Namespace List
* @NVME_LOG_LID_FDP_CONFIGS: FDP Configurations
* @NVME_LOG_LID_FDP_RUH_USAGE: Reclaim Unit Handle Usage
* @NVME_LOG_LID_FDP_STATS: FDP Statistics
* @NVME_LOG_LID_FDP_EVENTS: FDP Events
* @NVME_LOG_LID_DISCOVER: Discovery
+ * @NVME_LOG_LID_HOST_DISCOVER: Host Discovery
+ * @NVME_LOG_LID_AVE_DISCOVER: AVE Discovery
+ * @NVME_LOG_LID_PULL_MODEL_DDC_REQ: Pull Model DDC Request
* @NVME_LOG_LID_RESERVATION: Reservation Notification
* @NVME_LOG_LID_SANITIZE: Sanitize Status
* @NVME_LOG_LID_ZNS_CHANGED_ZONES: Changed Zone List
@@ -7576,12 +7925,21 @@ enum nvme_cmd_get_log_lid {
NVME_LOG_LID_MI_CMD_SUPPORTED_EFFECTS = 0x13,
NVME_LOG_LID_CMD_AND_FEAT_LOCKDOWN = 0x14,
NVME_LOG_LID_BOOT_PARTITION = 0x15,
+ NVME_LOG_LID_ROTATIONAL_MEDIA_INFO = 0x16,
+ NVME_LOG_LID_DISPERSED_NS_PARTICIPATING_NSS = 0x17,
+ NVME_LOG_LID_MGMT_ADDR_LIST = 0x18,
NVME_LOG_LID_PHY_RX_EOM = 0x19,
+ NVME_LOG_LID_REACHABILITY_GROUPS = 0x1a,
+ NVME_LOG_LID_REACHABILITY_ASSOCIATIONS = 0x1b,
+ NVME_LOG_LID_CHANGED_ALLOC_NS_LIST = 0x1c,
NVME_LOG_LID_FDP_CONFIGS = 0x20,
NVME_LOG_LID_FDP_RUH_USAGE = 0x21,
NVME_LOG_LID_FDP_STATS = 0x22,
NVME_LOG_LID_FDP_EVENTS = 0x23,
NVME_LOG_LID_DISCOVER = 0x70,
+ NVME_LOG_LID_HOST_DISCOVER = 0x71,
+ NVME_LOG_LID_AVE_DISCOVER = 0x72,
+ NVME_LOG_LID_PULL_MODEL_DDC_REQ = 0x73,
NVME_LOG_LID_RESERVATION = 0x80,
NVME_LOG_LID_SANITIZE = 0x81,
NVME_LOG_LID_ZNS_CHANGED_ZONES = 0xbf,
@@ -8059,12 +8417,14 @@ enum nvme_directive_send_identify_endir {
* @NVME_SANITIZE_SANACT_START_BLOCK_ERASE: Start a Block Erase sanitize operation.
* @NVME_SANITIZE_SANACT_START_OVERWRITE: Start an Overwrite sanitize operation.
* @NVME_SANITIZE_SANACT_START_CRYPTO_ERASE: Start a Crypto Erase sanitize operation.
+ * @NVME_SANITIZE_SANACT_EXIT_MEDIA_VERIF: Exit Media Verification State
*/
enum nvme_sanitize_sanact {
NVME_SANITIZE_SANACT_EXIT_FAILURE = 1,
NVME_SANITIZE_SANACT_START_BLOCK_ERASE = 2,
NVME_SANITIZE_SANACT_START_OVERWRITE = 3,
NVME_SANITIZE_SANACT_START_CRYPTO_ERASE = 4,
+ NVME_SANITIZE_SANACT_EXIT_MEDIA_VERIF = 5,
};
/**
@@ -8323,6 +8683,32 @@ enum nvme_io_opcode {
};
/**
+ * enum nvme_kv_opcode - Opcodes for KV Commands
+ * @nvme_kv_cmd_flush: Flush
+ * @nvme_kv_cmd_store: Store
+ * @nvme_kv_cmd_retrieve: Retrieve
+ * @nvme_kv_cmd_list: List
+ * @nvme_kv_cmd_resv_register: Reservation Register
+ * @nvme_kv_cmd_resv_report: Reservation Report
+ * @nvme_kv_cmd_delete: Delete
+ * @nvme_kv_cmd_resv_acquire: Reservation Acquire
+ * @nvme_kv_cmd_exist: Exist
+ * @nvme_kv_cmd_resv_release: Reservation Release
+ */
+enum nvme_kv_opcode {
+ nvme_kv_cmd_flush = 0x00,
+ nvme_kv_cmd_store = 0x01,
+ nvme_kv_cmd_retrieve = 0x02,
+ nvme_kv_cmd_list = 0x06,
+ nvme_kv_cmd_resv_register = 0x0d,
+ nvme_kv_cmd_resv_report = 0x0e,
+ nvme_kv_cmd_delete = 0x10,
+ nvme_kv_cmd_resv_acquire = 0x11,
+ nvme_kv_cmd_exist = 0x14,
+ nvme_kv_cmd_resv_release = 0x15,
+};
+
+/**
* enum nvme_io_control_flags - I/O control flags
* @NVME_IO_DTYPE_STREAMS: Directive Type Streams
* @NVME_IO_STC: Storage Tag Check
diff --git a/src/nvme/util.c b/src/nvme/util.c
index ce0ce76..1af358a 100644
--- a/src/nvme/util.c
+++ b/src/nvme/util.c
@@ -286,6 +286,11 @@ static const char * const cmd_spec_status[] = {
[NVME_SC_INSUFFICIENT_DISC_RES] = "Discovery Info entries exceed Discovery Controller's capacity",
[NVME_SC_REQSTD_FUNCTION_DISABLED] = "Fabric Zoning is not enabled on the CDC",
[NVME_SC_ZONEGRP_ORIGINATOR_INVLD] = "The NQN contained in the ZoneGroup Originator field does not match the Host NQN used by the DDC to connect to the CDC",
+ [NVME_SC_INVALID_CONTROLER_DATA_QUEUE] = "Invalid Controller Data Queue",
+ [NVME_SC_NOT_ENOUGH_RESOURCES] = "Not Enough Resources",
+ [NVME_SC_CONTROLLER_SUSPENDED] = "Controller Suspended: Operation failed because the controller is currently in a suspended state",
+ [NVME_SC_CONTROLLER_NOT_SUSPENDED] = "Controller Not Suspended: Operation failed because the controller is not in a suspended state",
+ [NVME_SC_CONTROLLER_DATA_QUEUE_FULL] = "Controller Data Queue Full",
};
static const char * const nvm_status[] = {
@@ -621,6 +626,7 @@ static const char * const libnvme_status[] = {
[ENVME_CONNECT_CONNREFUSED] = "connection refused",
[ENVME_CONNECT_ADDRNOTAVAIL] = "cannot assign requested address",
[ENVME_CONNECT_IGNORED] = "connection ignored",
+ [ENVME_CONNECT_NOKEY] = "pre-shared TLS key is missing"
};
const char *nvme_errno_to_string(int status)
diff --git a/src/nvme/util.h b/src/nvme/util.h
index 71fea9f..364ca0f 100644
--- a/src/nvme/util.h
+++ b/src/nvme/util.h
@@ -41,6 +41,7 @@
* @ENVME_CONNECT_CONNREFUSED: connection refused
* @ENVME_CONNECT_ADDRNOTAVAIL: cannot assign requested address
* @ENVME_CONNECT_IGNORED: connect attempt is ignored due to configuration
+ * @ENVME_CONNECT_NOKEY: the TLS key is missing
*/
enum nvme_connect_err {
ENVME_CONNECT_RESOLVE = 1000,
@@ -63,6 +64,7 @@ enum nvme_connect_err {
ENVME_CONNECT_CONNREFUSED,
ENVME_CONNECT_ADDRNOTAVAIL,
ENVME_CONNECT_IGNORED,
+ ENVME_CONNECT_NOKEY,
};
/**