diff options
Diffstat (limited to 'test')
-rwxr-xr-x[-rw-r--r--] | test/config/config-diff.sh | 52 | ||||
-rw-r--r-- | test/config/data/tls_key-1.json | 21 | ||||
-rw-r--r-- | test/config/data/tls_key-1.out | 21 | ||||
-rw-r--r-- | test/config/data/tls_key-2.json | 22 | ||||
-rw-r--r-- | test/config/data/tls_key-2.out | 21 | ||||
-rw-r--r-- | test/config/meson.build | 45 | ||||
-rw-r--r-- | test/config/psk-json.c | 89 | ||||
-rw-r--r-- | test/meson.build | 9 | ||||
-rw-r--r-- | test/mi.c | 99 | ||||
-rw-r--r-- | test/psk.c | 225 |
10 files changed, 582 insertions, 22 deletions
diff --git a/test/config/config-diff.sh b/test/config/config-diff.sh index c15b42c..abec20e 100644..100755 --- a/test/config/config-diff.sh +++ b/test/config/config-diff.sh @@ -1,24 +1,48 @@ #!/bin/bash -e # SPDX-License-Identifier: LGPL-2.1-or-later -BUILD_DIR=$1 -CONFIG_DUMP=$2 -SYSDIR_INPUT=$3 -CONFIG_JSON=$4 -EXPECTED_OUTPUT=$5 +positional_args=() +sysfs_tar="" +config_json="" -ACTUAL_OUTPUT="${BUILD_DIR}"/$(basename "${EXPECTED_OUTPUT}") +while [[ $# -gt 0 ]]; do + case $1 in + --sysfs-tar) + sysfs_tar=$2 + shift 1 + ;; + --config-json) + config_json=$2 + shift 1 + ;; + *) + positional_args+=("$1") + shift + ;; + esac +done -TEST_NAME="$(basename -s .tar.xz $SYSDIR_INPUT)" -TEST_DIR="$BUILD_DIR/$TEST_NAME" +set -- "${positional_args[@]}" -rm -rf "${TEST_DIR}" -mkdir "${TEST_DIR}" -tar -x -f "${SYSDIR_INPUT}" -C "${TEST_DIR}" +test_binary="$1" +build_dir="$2" +expected_output="$3" -LIBNVME_SYSFS_PATH="$TEST_DIR" \ +sysfs_path="" +if [[ -n "${sysfs_tar}" ]]; then + test_name="$(basename -s .tar.xz ${sysfs_tar})" + sysfs_path="${build_dir}/${test_name}" + + rm -rf "${sysfs_path}" + mkdir "${sysfs_path}" + tar -x -f "${sysfs_tar}" -C "${sysfs_path}" +fi + +output="${build_dir}"/$(basename "${expected_output}") + +LIBNVME_SYSFS_PATH="${sysfs_path}" \ LIBNVME_HOSTNQN=nqn.2014-08.org.nvmexpress:uuid:ce4fee3e-c02c-11ee-8442-830d068a36c6 \ LIBNVME_HOSTID=ce4fee3e-c02c-11ee-8442-830d068a36c6 \ -"${CONFIG_DUMP}" "${CONFIG_JSON}" > "${ACTUAL_OUTPUT}" || echo "test failed" +"${test_binary}" "${config_json}" > "${output}" || echo "test failed" -diff -u "${EXPECTED_OUTPUT}" "${ACTUAL_OUTPUT}" +diff -u "${expected_output}" "${output}" diff --git a/test/config/data/tls_key-1.json b/test/config/data/tls_key-1.json new file mode 100644 index 0000000..5bba948 --- /dev/null +++ b/test/config/data/tls_key-1.json @@ -0,0 +1,21 @@ +[ + { + "hostnqn":"nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36", + "hostid":"2cd2c43b-a90a-45c1-a8cd-86b33ab273b6", + "subsystems":[ + { + "nqn":"nqn.io-1", + "ports":[ + { + "transport":"tcp", + "traddr":"192.168.154.148", + "trsvcid":"4420", + "dhchap_key":"none", + "tls":true, + "tls_key":"NVMeTLSkey-1:01:Hhc5sFjwSZ6w5hPY19tqprajYtuYci3tN+Z2wGViDk3rpSR+:" + } + ] + } + ] + } +] diff --git a/test/config/data/tls_key-1.out b/test/config/data/tls_key-1.out new file mode 100644 index 0000000..5bba948 --- /dev/null +++ b/test/config/data/tls_key-1.out @@ -0,0 +1,21 @@ +[ + { + "hostnqn":"nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36", + "hostid":"2cd2c43b-a90a-45c1-a8cd-86b33ab273b6", + "subsystems":[ + { + "nqn":"nqn.io-1", + "ports":[ + { + "transport":"tcp", + "traddr":"192.168.154.148", + "trsvcid":"4420", + "dhchap_key":"none", + "tls":true, + "tls_key":"NVMeTLSkey-1:01:Hhc5sFjwSZ6w5hPY19tqprajYtuYci3tN+Z2wGViDk3rpSR+:" + } + ] + } + ] + } +] diff --git a/test/config/data/tls_key-2.json b/test/config/data/tls_key-2.json new file mode 100644 index 0000000..9d6ce62 --- /dev/null +++ b/test/config/data/tls_key-2.json @@ -0,0 +1,22 @@ +[ + { + "hostnqn":"nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36", + "hostid":"2cd2c43b-a90a-45c1-a8cd-86b33ab273b6", + "subsystems":[ + { + "nqn":"nqn.io-1", + "ports":[ + { + "transport":"tcp", + "traddr":"192.168.154.148", + "trsvcid":"4420", + "dhchap_key":"none", + "tls":true, + "tls_psk_identity":"NVMe1R01 nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36 nqn.io-1 QMFIifx2SCVnlE2hc4MQb0r+2g56x3G7P6jJtDiYK+I=", + "tls_key":"NVMeTLSkey-1:01:Hhc5sFjwSZ6w5hPY19tqprajYtuYci3tN+Z2wGViDk3rpSR+:" + } + ] + } + ] + } +] diff --git a/test/config/data/tls_key-2.out b/test/config/data/tls_key-2.out new file mode 100644 index 0000000..5bba948 --- /dev/null +++ b/test/config/data/tls_key-2.out @@ -0,0 +1,21 @@ +[ + { + "hostnqn":"nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36", + "hostid":"2cd2c43b-a90a-45c1-a8cd-86b33ab273b6", + "subsystems":[ + { + "nqn":"nqn.io-1", + "ports":[ + { + "transport":"tcp", + "traddr":"192.168.154.148", + "trsvcid":"4420", + "dhchap_key":"none", + "tls":true, + "tls_key":"NVMeTLSkey-1:01:Hhc5sFjwSZ6w5hPY19tqprajYtuYci3tN+Z2wGViDk3rpSR+:" + } + ] + } + ] + } +] diff --git a/test/config/meson.build b/test/config/meson.build index c1ee7ca..aeb5ccf 100644 --- a/test/config/meson.build +++ b/test/config/meson.build @@ -7,6 +7,10 @@ diff = find_program('diff', required : false) if diff.found() + + srcdir = meson.current_source_dir() + builddir = meson.current_build_dir() + config_dump = executable( 'test-config-dump', ['config-dump.c'], @@ -26,11 +30,11 @@ if diff.found() t_file, config_diff, args : [ - meson.current_build_dir(), config_dump.full_path(), - files('data'/t_file + '.tar.xz'), - files('data'/t_file + '.json'), - files('data'/t_file + '.out'), + builddir, + srcdir + '/data/' + t_file + '.out', + '--sysfs-tar', srcdir + '/data/' + t_file + '.tar.xz', + '--config-json', srcdir + '/data/' + t_file + '.json', ], depends : config_dump, ) @@ -47,13 +51,38 @@ if diff.found() 'hostnqn-order', config_diff, args : [ - meson.current_build_dir(), test_hostnqn_order.full_path(), - files('data/hostnqn-order.tar.xz'), - files('data/hostnqn-order.json'), - files('data/hostnqn-order.out'), + builddir, + srcdir + '/data/hostnqn-order.out', + '--sysfs-tar', srcdir + '/data/hostnqn-order.tar.xz', + '--config-json', srcdir + '/data/hostnqn-order.json', ], depends : test_hostnqn_order, ) + test_psk_json = executable( + 'test-psk-json', + ['psk-json.c'], + dependencies: libnvme_dep, + include_directories: [incdir], + ) + + config_data = [ + 'tls_key-1', + 'tls_key-2', + ] + + foreach t_file : config_data + test( + 'psk-json-' + t_file, + config_diff, + args : [ + test_psk_json.full_path(), + builddir, + srcdir + '/data/' + t_file + '.out', + '--config-json', srcdir + '/data/' + t_file + '.json', + ], + depends : test_psk_json, + ) + endforeach endif diff --git a/test/config/psk-json.c b/test/config/psk-json.c new file mode 100644 index 0000000..a96ebf1 --- /dev/null +++ b/test/config/psk-json.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/** + * This file is part of libnvme. + * Copyright (c) 2024 Daniel Wagner, SUSE LLC + */ + +#include "nvme/linux.h" +#include "nvme/tree.h" +#include <string.h> +#include <stdbool.h> +#include <stdlib.h> +#include <errno.h> + +#include <libnvme.h> + +static bool import_export_key(nvme_ctrl_t c) +{ + unsigned char version, hmac, *key; + char *encoded_key; + size_t len; + + key = nvme_import_tls_key_versioned(nvme_ctrl_get_tls_key(c), + &version, &hmac, &len); + if (!key) { + printf("ERROR: nvme_import_tls_key_versioned failed with %d\n", + errno); + return false; + + } + + encoded_key = nvme_export_tls_key_versioned(version, hmac, key, len); + free(key); + if (!encoded_key) { + printf("ERROR: nvme_export_tls_key_versioned failed with %d\n", + errno); + return false; + } + + nvme_ctrl_set_tls_key(c, encoded_key); + + free(encoded_key); + + return true; +} + +static bool psk_json_test(char *file) +{ + bool pass = false; + nvme_root_t r; + nvme_host_t h; + nvme_subsystem_t s; + nvme_ctrl_t c; + int err; + + r = nvme_create_root(stderr, LOG_ERR); + if (!r) + return false; + + err = nvme_read_config(r, file); + if (err) + goto out; + + + nvme_for_each_host(r, h) + nvme_for_each_subsystem(h, s) + nvme_subsystem_for_each_ctrl(s, c) + if (!import_export_key(c)) + goto out; + + err = nvme_dump_config(r); + if (err) + goto out; + + pass = true; + +out: + nvme_free_tree(r); + return pass; +} + +int main(int argc, char *argv[]) +{ + bool pass; + + pass = psk_json_test(argv[1]); + fflush(stdout); + + exit(pass ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/test/meson.build b/test/meson.build index ca2a792..2ab7e31 100644 --- a/test/meson.build +++ b/test/meson.build @@ -104,6 +104,15 @@ if conf.get('HAVE_NETDB') test('util', test_util) endif +psk = executable( + 'test-psk', + ['psk.c'], + dependencies: libnvme_dep, + include_directories: [incdir, internal_incdir] +) + +test('psk', psk) + subdir('ioctl') subdir('nbft') @@ -1908,6 +1908,103 @@ static void test_endpoint_quirk_probe(struct nvme_mi_ep *ep) assert(rc == 0); } +struct req_dlen_doff_data { + enum { + DATA_DIR_IN, + DATA_DIR_OUT, + } direction; + unsigned int req_len; + unsigned int resp_len; + unsigned int exp_doff; +}; + +static int test_admin_dlen_doff_cb(struct nvme_mi_ep *ep, + struct nvme_mi_req *req, + struct nvme_mi_resp *resp, + void *data) +{ + struct req_dlen_doff_data *args = data; + __u8 *hdr = (__u8 *)req->hdr; + __u32 dlen, doff; + + dlen = hdr[35] << 24 | hdr[34] << 16 | hdr[33] << 8 | hdr[32]; + doff = hdr[39] << 24 | hdr[38] << 16 | hdr[37] << 8 | hdr[36]; + + if (args->direction == DATA_DIR_OUT) { + assert(dlen == args->req_len); + assert(dlen == req->data_len); + assert(doff == 0); + } else { + assert(dlen == args->resp_len); + assert(dlen == resp->data_len); + assert(doff == args->exp_doff); + } + + /* minimal valid response */ + hdr = (__u8 *)resp->hdr; + hdr[4] = 0x00; /* status: success */ + + test_transport_resp_calc_mic(resp); + + return 0; +} + +/* Check dlen value on admin_xfer requests that include data. */ +static void test_admin_dlen_doff_req(struct nvme_mi_ep *ep) +{ + struct { + struct nvme_mi_admin_req_hdr hdr; + unsigned char data[4096]; + } admin_req = { 0 }; + struct nvme_mi_admin_resp_hdr admin_resp = { 0 }; + struct req_dlen_doff_data data = { 0 }; + size_t resp_sz = 0; + nvme_mi_ctrl_t ctrl; + int rc; + + data.direction = DATA_DIR_OUT; + data.req_len = sizeof(admin_req.data); + + test_set_transport_callback(ep, test_admin_dlen_doff_cb, &data); + + ctrl = nvme_mi_init_ctrl(ep, 0); + assert(ctrl); + + rc = nvme_mi_admin_xfer(ctrl, &admin_req.hdr, sizeof(admin_req.data), + &admin_resp, 0, &resp_sz); + + assert(!rc); +}; + +/* Check dlen value on admin_xfer requests that return data in their response. + */ +static void test_admin_dlen_doff_resp(struct nvme_mi_ep *ep) +{ + struct { + struct nvme_mi_admin_resp_hdr hdr; + unsigned char data[4096]; + } admin_resp = { 0 }; + struct nvme_mi_admin_req_hdr admin_req = { 0 }; + struct req_dlen_doff_data data = { 0 }; + nvme_mi_ctrl_t ctrl; + size_t resp_sz; + int rc; + + data.direction = DATA_DIR_IN; + data.resp_len = sizeof(admin_resp.data); + resp_sz = sizeof(admin_resp.data); + + test_set_transport_callback(ep, test_admin_dlen_doff_cb, &data); + + ctrl = nvme_mi_init_ctrl(ep, 0); + assert(ctrl); + + rc = nvme_mi_admin_xfer(ctrl, &admin_req, 0, &admin_resp.hdr, 0, + &resp_sz); + + assert(!rc); +}; + #define DEFINE_TEST(name) { #name, test_ ## name } struct test { const char *name; @@ -1950,6 +2047,8 @@ struct test { DEFINE_TEST(admin_sanitize_nvm), DEFINE_TEST(admin_get_log_split), DEFINE_TEST(endpoint_quirk_probe), + DEFINE_TEST(admin_dlen_doff_req), + DEFINE_TEST(admin_dlen_doff_resp), }; static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep) diff --git a/test/psk.c b/test/psk.c new file mode 100644 index 0000000..02cb6d8 --- /dev/null +++ b/test/psk.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/** + * This file is part of libnvme. + * Copyright (c) 2024 Daniel Wagner, SUSE Software Solutions + */ + +#include "nvme/linux.h" +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <ccan/array_size/array_size.h> + +#include <libnvme.h> + +static int test_rc; + +struct test_data { + const unsigned char configured_psk[48]; + size_t psk_length; + unsigned char version; + unsigned char hmac; + const char *exported_psk; +}; + +static struct test_data test_data[] = { + { { 0x55, 0x12, 0xDB, 0xB6, + 0x73, 0x7D, 0x01, 0x06, + 0xF6, 0x59, 0x75, 0xB7, + 0x73, 0xDF, 0xB0, 0x11, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03}, + 32, 1, NVME_HMAC_ALG_NONE, + "NVMeTLSkey-1:00:VRLbtnN9AQb2WXW3c9+wEf/DRLz0QuLdbYvEhwtdWwNf9LrZ:" }, + { { 0x55, 0x12, 0xDB, 0xB6, + 0x73, 0x7D, 0x01, 0x06, + 0xF6, 0x59, 0x75, 0xB7, + 0x73, 0xDF, 0xB0, 0x11, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03}, + 32, 1, NVME_HMAC_ALG_SHA2_256, + "NVMeTLSkey-1:01:VRLbtnN9AQb2WXW3c9+wEf/DRLz0QuLdbYvEhwtdWwNf9LrZ:" }, + { { 0x55, 0x12, 0xDB, 0xB6, + 0x73, 0x7D, 0x01, 0x06, + 0xF6, 0x59, 0x75, 0xB7, + 0x73, 0xDF, 0xB0, 0x11, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03}, + 48, 1, NVME_HMAC_ALG_SHA2_384, + "NVMeTLSkey-1:02:VRLbtnN9AQb2WXW3c9+wEf/DRLz0QuLdbYvEhwtdWwP/w0S89ELi3W2LxIcLXVsDn8kXZQ==:" }, +}; + +static void check_str(const char *exp, const char *res) +{ + if (!strcmp(res, exp)) + return; + + printf("ERROR: got '%s', expected '%s'\n", res, exp); + + test_rc = 1; +} + +static void export_test(struct test_data *test) +{ + char *psk; + + if (test->version != 1 || + !(test->hmac == NVME_HMAC_ALG_SHA2_256 || + test->hmac == NVME_HMAC_ALG_SHA2_384)) + return; + + printf("test nvme_export_tls_key hmac %d %s\n", + test->hmac, test->exported_psk); + + psk = nvme_export_tls_key(test->configured_psk, test->psk_length); + if (!psk) { + test_rc = 1; + printf("ERROR: nvme_export_tls_key() failed with %d\n", errno); + return; + } + check_str(test->exported_psk, psk); + free(psk); +} + +static void import_test(struct test_data *test) +{ + unsigned char *psk; + int psk_length; + unsigned int hmac; + + if (test->version != 1 || + !(test->hmac == NVME_HMAC_ALG_SHA2_256 || + test->hmac == NVME_HMAC_ALG_SHA2_384)) + return; + + printf("test nvme_import_tls_key hmac %d %s\n", + test->hmac, test->exported_psk); + + psk = nvme_import_tls_key(test->exported_psk, &psk_length, &hmac); + if (!psk) { + test_rc = 1; + printf("ERROR: nvme_import_tls_key() failed with %d\n", errno); + return; + } + + if (test->hmac != hmac) { + test_rc = 1; + printf("ERROR: hmac parsing failed\n"); + goto out; + } + + if (test->psk_length != psk_length) { + test_rc = 1; + printf("ERROR: length parsing failed\n"); + goto out; + } + if (memcmp(test->configured_psk, psk, psk_length)) { + test_rc = 1; + printf("ERROR: parsing psk failed\n"); + } + +out: + free(psk); +} + +static void export_versioned_test(struct test_data *test) +{ + char *psk; + + if (test->version != 1) + return; + + printf("test nvme_export_tls_key_versioned hmac %d %s\n", + test->hmac, test->exported_psk); + + psk = nvme_export_tls_key_versioned(test->version, test->hmac, + test->configured_psk, + test->psk_length); + if (!psk) { + test_rc = 1; + printf("ERROR: nvme_export_tls_key_versioned() failed with %d\n", + errno); + return; + } + + check_str(test->exported_psk, psk); + + free(psk); +} + +static void import_versioned_test(struct test_data *test) +{ + unsigned char *psk; + unsigned char version; + unsigned char hmac; + size_t psk_length; + + if (test->version != 1) + return; + + printf("test nvme_import_tls_key_versioned hmac %d %s\n", + test->hmac, test->exported_psk); + + psk = nvme_import_tls_key_versioned(test->exported_psk, &version, + &hmac, &psk_length); + if (!psk) { + test_rc = 1; + printf("ERROR: nvme_import_tls_key_versioned() failed with %d\n", + errno); + return; + } + + if (test->version != version) { + test_rc = 1; + printf("ERROR: version parsing failed\n"); + goto out; + } + + if (test->hmac != hmac) { + test_rc = 1; + printf("ERROR: hmac parsing failed\n"); + goto out; + } + + if (test->psk_length != psk_length) { + test_rc = 1; + printf("ERROR: length parsing failed\n"); + goto out; + } + + if (memcmp(test->configured_psk, psk, psk_length)) { + test_rc = 1; + printf("ERROR: parsing psk failed\n"); + } + +out: + free(psk); +} + +int main(void) +{ + for (int i = 0; i < ARRAY_SIZE(test_data); i++) + export_test(&test_data[i]); + + for (int i = 0; i < ARRAY_SIZE(test_data); i++) + import_test(&test_data[i]); + + for (int i = 0; i < ARRAY_SIZE(test_data); i++) + export_versioned_test(&test_data[i]); + + for (int i = 0; i < ARRAY_SIZE(test_data); i++) + import_versioned_test(&test_data[i]); + + return test_rc ? EXIT_FAILURE : EXIT_SUCCESS; +} |