Commit 663243cb authored by Krzysztof Karas's avatar Krzysztof Karas Committed by Jim Harris
Browse files

nvmf: add implementation of subsystem_add/remove_host()



Add implementation of previously introduced subsystem_add_host()
and subsystem_remove_host() callback in TCP transport.

nvmf_subystem_add_host() generates PSK identity based on
hostnqn and subnqn and stores it in a list inside nvmf
TCP transport.

nvmf_subsystem_remove_host() searches for a pair
(subnqn, hostnqn) in the list and removes that entry.

Change-Id: I244dd1b3eca7dd58896e1ee463e04865810c3916
Signed-off-by: default avatarKrzysztof Karas <krzysztof.karas@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15448


Reviewed-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarAleksey Marchuk <alexeymar@nvidia.com>
Community-CI: Mellanox Build Bot
parent 9ec93fbb
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -49,6 +49,18 @@
         ((*((uint8_t *)(B)+2)) = (uint8_t)((uint32_t)(D) >> 16)),              \
         ((*((uint8_t *)(B)+3)) = (uint8_t)((uint32_t)(D) >> 24)))

/* The PSK identity comprises of following components:
 * 4-character format specifier "NVMe" +
 * 1-character TLS protocol version indicator +
 * 1-character PSK type indicator, specifying the used PSK +
 * 2-characters hash specifier +
 * NQN of the host (SPDK_NVMF_NQN_MAX_LEN -> 223) +
 * NQN of the subsystem (SPDK_NVMF_NQN_MAX_LEN -> 223) +
 * 2 space character separators +
 * 1 null terminator =
 * 457 characters. */
#define NVMF_PSK_IDENTITY_LEN (SPDK_NVMF_NQN_MAX_LEN + SPDK_NVMF_NQN_MAX_LEN + 11)

typedef void (*nvme_tcp_qpair_xfer_complete_cb)(void *cb_arg);

struct nvme_tcp_pdu {
+146 −0
Original line number Diff line number Diff line
@@ -342,6 +342,14 @@ struct tcp_transport_opts {
	uint32_t	sock_priority;
};

struct tcp_psk_entry {
	char				hostnqn[SPDK_NVMF_NQN_MAX_LEN + 1];
	char				subnqn[SPDK_NVMF_NQN_MAX_LEN + 1];
	char				psk_identity[NVMF_PSK_IDENTITY_LEN];
	uint8_t				psk[SPDK_TLS_PSK_MAX_LEN];
	TAILQ_ENTRY(tcp_psk_entry)	link;
};

struct spdk_nvmf_tcp_transport {
	struct spdk_nvmf_transport		transport;
	struct tcp_transport_opts               tcp_opts;
@@ -352,6 +360,8 @@ struct spdk_nvmf_tcp_transport {

	TAILQ_HEAD(, spdk_nvmf_tcp_port)	ports;
	TAILQ_HEAD(, spdk_nvmf_tcp_poll_group)	poll_groups;

	TAILQ_HEAD(, tcp_psk_entry)		psks;
};

static const struct spdk_json_object_decoder tcp_transport_opts_decoder[] = {
@@ -584,10 +594,16 @@ nvmf_tcp_destroy(struct spdk_nvmf_transport *transport,
		 spdk_nvmf_transport_destroy_done_cb cb_fn, void *cb_arg)
{
	struct spdk_nvmf_tcp_transport	*ttransport;
	struct tcp_psk_entry *entry, *tmp;

	assert(transport != NULL);
	ttransport = SPDK_CONTAINEROF(transport, struct spdk_nvmf_tcp_transport, transport);

	TAILQ_FOREACH_SAFE(entry, &ttransport->psks, link, tmp) {
		TAILQ_REMOVE(&ttransport->psks, entry, link);
		free(entry);
	}

	spdk_poller_unregister(&ttransport->accept_poller);
	free(ttransport);

@@ -613,6 +629,7 @@ nvmf_tcp_create(struct spdk_nvmf_transport_opts *opts)

	TAILQ_INIT(&ttransport->ports);
	TAILQ_INIT(&ttransport->poll_groups);
	TAILQ_INIT(&ttransport->psks);

	ttransport->transport.ops = &spdk_nvmf_transport_tcp;

@@ -3423,6 +3440,133 @@ nvmf_tcp_qpair_abort_request(struct spdk_nvmf_qpair *qpair,
	_nvmf_tcp_qpair_abort_request(req);
}

struct tcp_subsystem_add_host_opts {
	char *psk;
};

static const struct spdk_json_object_decoder tcp_subsystem_add_host_opts_decoder[] = {
	{"psk", offsetof(struct tcp_subsystem_add_host_opts, psk), spdk_json_decode_string, true},
};

static int
nvmf_tcp_subsystem_add_host(struct spdk_nvmf_transport *transport,
			    const struct spdk_nvmf_subsystem *subsystem,
			    const char *hostnqn,
			    const struct spdk_json_val *transport_specific)
{
	struct tcp_subsystem_add_host_opts opts;
	struct spdk_nvmf_tcp_transport *ttransport;
	struct tcp_psk_entry *entry;
	/* This hardcoded PSK identity prefix will remain until
	 * support for different hash functions to generate PSK
	 * is introduced. */
	const char *psk_id_prefix = "NVMe0R01";
	char psk_identity[NVMF_PSK_IDENTITY_LEN];
	uint64_t key_len;
	int rc = 0;

	if (transport_specific == NULL) {
		return 0;
	}

	assert(transport != NULL);
	assert(subsystem != NULL);

	memset(&opts, 0, sizeof(opts));

	/* Decode PSK */
	if (spdk_json_decode_object_relaxed(transport_specific, tcp_subsystem_add_host_opts_decoder,
					    SPDK_COUNTOF(tcp_subsystem_add_host_opts_decoder), &opts)) {
		SPDK_ERRLOG("spdk_json_decode_object failed\n");
		return -EINVAL;
	}

	if (opts.psk == NULL) {
		return 0;
	}

	ttransport = SPDK_CONTAINEROF(transport, struct spdk_nvmf_tcp_transport, transport);
	/* Generate PSK identity. */
	if (snprintf(psk_identity, NVMF_PSK_IDENTITY_LEN, "%s %s %s", psk_id_prefix, hostnqn,
		     subsystem->subnqn) < 0) {
		SPDK_ERRLOG("Could not construct PSK identity string!\n");
		rc = -EINVAL;
		goto end;
	}
	/* Check if PSK identity entry already exists. */
	TAILQ_FOREACH(entry, &ttransport->psks, link) {
		if (strncmp(entry->psk_identity, psk_identity, NVMF_PSK_IDENTITY_LEN) == 0) {
			SPDK_ERRLOG("Given PSK identity: %s entry already exists!\n", psk_identity);
			rc = -EEXIST;
			goto end;
		}
	}
	entry = calloc(1, sizeof(struct tcp_psk_entry));
	if (entry == NULL) {
		SPDK_ERRLOG("Unable to allocate memory for PSK entry!\n");
		rc = -ENOMEM;
		goto end;
	}
	if (snprintf(entry->hostnqn, sizeof(entry->hostnqn), "%s", hostnqn) < 0) {
		SPDK_ERRLOG("Could not write hostnqn string!\n");
		rc = -EINVAL;
		free(entry);
		goto end;
	}
	if (snprintf(entry->subnqn, sizeof(entry->subnqn), "%s", subsystem->subnqn) < 0) {
		SPDK_ERRLOG("Could not write subnqn string!\n");
		rc = -EINVAL;
		free(entry);
		goto end;
	}
	if (snprintf(entry->psk_identity, sizeof(entry->psk_identity), "%s", psk_identity) < 0) {
		SPDK_ERRLOG("Could not write PSK identity string!\n");
		rc = -EINVAL;
		free(entry);
		goto end;
	}
	if (strlen(opts.psk) >= sizeof(entry->psk)) {
		SPDK_ERRLOG("PSK of length: %ld cannot fit in max buffer size: %ld\n", strlen(opts.psk),
			    sizeof(entry->psk));
		rc = -EINVAL;
		free(entry);
		goto end;
	}
	memcpy(entry->psk, opts.psk, strlen(opts.psk));

	TAILQ_INSERT_TAIL(&ttransport->psks, entry, link);

end:
	key_len = strnlen(opts.psk, SPDK_TLS_PSK_MAX_LEN);
	spdk_memset_s(opts.psk, key_len, 0, key_len);
	free(opts.psk);

	return rc;
}

static void
nvmf_tcp_subsystem_remove_host(struct spdk_nvmf_transport *transport,
			       const struct spdk_nvmf_subsystem *subsystem,
			       const char *hostnqn)
{
	struct spdk_nvmf_tcp_transport *ttransport;
	struct tcp_psk_entry *entry, *tmp;

	assert(transport != NULL);
	assert(subsystem != NULL);

	ttransport = SPDK_CONTAINEROF(transport, struct spdk_nvmf_tcp_transport, transport);
	TAILQ_FOREACH_SAFE(entry, &ttransport->psks, link, tmp) {
		if ((strncmp(entry->hostnqn, hostnqn, SPDK_NVMF_NQN_MAX_LEN)) == 0 &&
		    (strncmp(entry->subnqn, subsystem->subnqn, SPDK_NVMF_NQN_MAX_LEN)) == 0) {
			TAILQ_REMOVE(&ttransport->psks, entry, link);
			spdk_memset_s(entry->psk, sizeof(entry->psk), 0, sizeof(entry->psk));
			free(entry);
			break;
		}
	}
}

static void
nvmf_tcp_opts_init(struct spdk_nvmf_transport_opts *opts)
{
@@ -3467,6 +3611,8 @@ const struct spdk_nvmf_transport_ops spdk_nvmf_transport_tcp = {
	.qpair_get_peer_trid = nvmf_tcp_qpair_get_peer_trid,
	.qpair_get_listen_trid = nvmf_tcp_qpair_get_listen_trid,
	.qpair_abort_request = nvmf_tcp_qpair_abort_request,
	.subsystem_add_host = nvmf_tcp_subsystem_add_host,
	.subsystem_remove_host = nvmf_tcp_subsystem_remove_host,
};

SPDK_NVMF_TRANSPORT_REGISTER(tcp, &spdk_nvmf_transport_tcp);
+5 −2
Original line number Diff line number Diff line
@@ -423,13 +423,14 @@ def nvmf_subsystem_remove_ns(client, nqn, nsid, tgt_name=None):
    return client.call('nvmf_subsystem_remove_ns', params)


def nvmf_subsystem_add_host(client, nqn, host, tgt_name=None):
def nvmf_subsystem_add_host(client, nqn, host, tgt_name=None, psk=None):
    """Add a host NQN to the list of allowed hosts.

    Args:
        nqn: Subsystem NQN.
        host: Host NQN to add to the list of allowed host NQNs
        tgt_name: name of the parent NVMe-oF target (optional).
        tgt_name: name of the parent NVMe-oF target (optional)
        psk: PSK for TLS authentication.

    Returns:
        True or False
@@ -439,6 +440,8 @@ def nvmf_subsystem_add_host(client, nqn, host, tgt_name=None):

    if tgt_name:
        params['tgt_name'] = tgt_name
    if psk:
        params['psk'] = psk

    return client.call('nvmf_subsystem_add_host', params)

+3 −1
Original line number Diff line number Diff line
@@ -2529,12 +2529,14 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse
        rpc.nvmf.nvmf_subsystem_add_host(args.client,
                                         nqn=args.nqn,
                                         host=args.host,
                                         tgt_name=args.tgt_name)
                                         tgt_name=args.tgt_name,
                                         psk=args.psk)

    p = subparsers.add_parser('nvmf_subsystem_add_host', help='Add a host to an NVMe-oF subsystem')
    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
    p.add_argument('host', help='Host NQN to allow')
    p.add_argument('-t', '--tgt-name', help='The name of the parent NVMe-oF target (optional)', type=str)
    p.add_argument('--psk', help='PSK for TLS authentication (optional). Only applicable for TCP transport.', type=str)
    p.set_defaults(func=nvmf_subsystem_add_host)

    def nvmf_subsystem_remove_host(args):
+71 −0
Original line number Diff line number Diff line
@@ -1263,6 +1263,76 @@ test_nvmf_tcp_pdu_ch_handle(void)
			  struct spdk_nvme_tcp_common_pdu_hdr));
}

static void
test_nvmf_tcp_tls_add_remove_credentials(void)
{
	struct spdk_thread *thread;
	struct spdk_nvmf_transport *transport;
	struct spdk_nvmf_tcp_transport *ttransport;
	struct spdk_nvmf_transport_opts opts;
	struct spdk_nvmf_subsystem subsystem;
	struct tcp_psk_entry *entry;
	const char subnqn[] = {"nqn.2016-06.io.spdk:cnode1"};
	const char hostnqn[] = {"nqn.2016-06.io.spdk:host1"};
	struct spdk_json_val psk_json[] = {
		{"", 2, SPDK_JSON_VAL_OBJECT_BEGIN},
		{"psk", 3, SPDK_JSON_VAL_NAME},
		{"1234567890ABCDEF", 16, SPDK_JSON_VAL_STRING},
		{"", 0, SPDK_JSON_VAL_OBJECT_END},
	};
	bool found = false;

	thread = spdk_thread_create(NULL, NULL);
	SPDK_CU_ASSERT_FATAL(thread != NULL);
	spdk_set_thread(thread);

	memset(&opts, 0, sizeof(opts));
	opts.max_queue_depth = UT_MAX_QUEUE_DEPTH;
	opts.max_qpairs_per_ctrlr = UT_MAX_QPAIRS_PER_CTRLR;
	opts.in_capsule_data_size = UT_IN_CAPSULE_DATA_SIZE;
	opts.max_io_size = UT_MAX_IO_SIZE;
	opts.io_unit_size = UT_IO_UNIT_SIZE;
	opts.max_aq_depth = UT_MAX_AQ_DEPTH;
	opts.num_shared_buffers = UT_NUM_SHARED_BUFFERS;
	transport = nvmf_tcp_create(&opts);

	memset(&subsystem, 0, sizeof(subsystem));
	snprintf(subsystem.subnqn, sizeof(subsystem.subnqn), "%s", subnqn);

	nvmf_tcp_subsystem_add_host(transport, &subsystem, hostnqn, psk_json);

	ttransport = SPDK_CONTAINEROF(transport, struct spdk_nvmf_tcp_transport, transport);
	TAILQ_FOREACH(entry, &ttransport->psks, link) {
		if ((strcmp(subnqn, entry->subnqn) == 0) &&
		    (strcmp(hostnqn, entry->hostnqn) == 0)) {
			found = true;
		}
	}

	CU_ASSERT(found == true);
	found = false;

	nvmf_tcp_subsystem_remove_host(transport, &subsystem, hostnqn);

	ttransport = SPDK_CONTAINEROF(transport, struct spdk_nvmf_tcp_transport, transport);
	TAILQ_FOREACH(entry, &ttransport->psks, link) {
		if ((strcmp(subnqn, entry->subnqn) == 0) &&
		    (strcmp(hostnqn, entry->hostnqn) == 0)) {
			found = true;
		}
	}

	CU_ASSERT(found == false);

	CU_ASSERT(nvmf_tcp_destroy(transport, NULL, NULL) == 0);

	spdk_thread_exit(thread);
	while (!spdk_thread_is_exited(thread)) {
		spdk_thread_poll(thread, 0, 0);
	}
	spdk_thread_destroy(thread);
}

int
main(int argc, char **argv)
{
@@ -1287,6 +1357,7 @@ main(int argc, char **argv)
	CU_ADD_TEST(suite, test_nvmf_tcp_check_xfer_type);
	CU_ADD_TEST(suite, test_nvmf_tcp_invalid_sgl);
	CU_ADD_TEST(suite, test_nvmf_tcp_pdu_ch_handle);
	CU_ADD_TEST(suite, test_nvmf_tcp_tls_add_remove_credentials);

	CU_basic_set_mode(CU_BRM_VERBOSE);
	CU_basic_run_tests();