Commit 8d3f8fb8 authored by Konrad Sztyber's avatar Konrad Sztyber
Browse files

bdev/nvme: add method for changing DH-HMAC-CHAP keys



This method will change the keys on all multipath controllers and force
reauthentication on all qpairs.  The high level flow looks like this:

 1) Set the keys on the first multipath controller.
 2) Authenticate admin qpair.
 3) Go over all IO channels and authenticate IO qpairs on each one.
 4) If there's no next multipath controller, we're done, otherwise set
    keys on that controller and go to 2).

Signed-off-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Change-Id: Iec0de19f1a9326cf32c9b27acaab117100616770
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/24236


Community-CI: Mellanox Build Bot
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Reviewed-by: default avatarShuhei Matsumoto <smatsumoto@nvidia.com>
parent 16daee89
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -4922,6 +4922,47 @@ Example response:
}
~~~

### bdev_nvme_set_keys {#rpc_bdev_nvme_set_keys}

Set DH-HMAC-CHAP keys and force (re)authentication on all connected qpairs across all multipath
controllers.  If none of the keys are provided, the keys will be cleared, meaning that any new
qpairs won't be authenticated.

If successful, existing qpairs won't be disconnected/reconnected.

#### Parameters

Name                    | Optional | Type        | Description
----------------------- | -------- | ----------- | -----------
name                    | Required | string      | Name of the NVMe controller
dhchap_key              | Optional | string      | DH-HMAC-CHAP key name (required if controller key is specified)
dhchap_ctrlr_key        | Optional | string      | DH-HMAC-CHAP controller key name

#### Example

Example request:

~~~json
{
  "method": "bdev_nvme_set_keys",
  "params": {
    "name": "nvme0",
    "dhchap_key": "key0",
    "dhchap_ctrlr_key": "key1"
  }
}
~~~

Example response:

~~~json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
~~~

### bdev_zone_block_create {#rpc_bdev_zone_block_create}

Creates a virtual zone device on top of existing non-zoned bdev.
+197 −0
Original line number Diff line number Diff line
@@ -8781,6 +8781,203 @@ nvme_io_path_is_current(struct nvme_io_path *io_path)
	return current;
}

struct bdev_nvme_set_keys_ctx {
	struct nvme_ctrlr	*nctrlr;
	struct spdk_key		*dhchap_key;
	struct spdk_key		*dhchap_ctrlr_key;
	struct spdk_thread	*thread;
	bdev_nvme_set_keys_cb	cb_fn;
	void			*cb_ctx;
	int			status;
};

static void
bdev_nvme_free_set_keys_ctx(struct bdev_nvme_set_keys_ctx *ctx)
{
	if (ctx == NULL) {
		return;
	}

	spdk_keyring_put_key(ctx->dhchap_key);
	spdk_keyring_put_key(ctx->dhchap_ctrlr_key);
	free(ctx);
}

static void
_bdev_nvme_set_keys_done(void *_ctx)
{
	struct bdev_nvme_set_keys_ctx *ctx = _ctx;

	ctx->cb_fn(ctx->cb_ctx, ctx->status);

	if (ctx->nctrlr != NULL) {
		nvme_ctrlr_release(ctx->nctrlr);
	}
	bdev_nvme_free_set_keys_ctx(ctx);
}

static void
bdev_nvme_set_keys_done(struct bdev_nvme_set_keys_ctx *ctx, int status)
{
	ctx->status = status;
	spdk_thread_exec_msg(ctx->thread, _bdev_nvme_set_keys_done, ctx);
}

static void bdev_nvme_authenticate_ctrlr(struct bdev_nvme_set_keys_ctx *ctx);

static void
bdev_nvme_authenticate_ctrlr_continue(struct bdev_nvme_set_keys_ctx *ctx)
{
	struct nvme_ctrlr *next;

	pthread_mutex_lock(&g_bdev_nvme_mutex);
	next = TAILQ_NEXT(ctx->nctrlr, tailq);
	if (next != NULL) {
		next->ref++;
	}
	pthread_mutex_unlock(&g_bdev_nvme_mutex);

	nvme_ctrlr_release(ctx->nctrlr);
	ctx->nctrlr = next;

	if (next == NULL) {
		bdev_nvme_set_keys_done(ctx, 0);
	} else {
		bdev_nvme_authenticate_ctrlr(ctx);
	}
}

static void
bdev_nvme_authenticate_qpairs_done(struct spdk_io_channel_iter *i, int status)
{
	struct bdev_nvme_set_keys_ctx *ctx = spdk_io_channel_iter_get_ctx(i);

	if (status != 0) {
		bdev_nvme_set_keys_done(ctx, status);
		return;
	}
	bdev_nvme_authenticate_ctrlr_continue(ctx);
}

static void
bdev_nvme_authenticate_qpair_done(void *ctx, int status)
{
	spdk_for_each_channel_continue(ctx, status);
}

static void
bdev_nvme_authenticate_qpair(struct spdk_io_channel_iter *i)
{
	struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
	struct nvme_ctrlr_channel *ctrlr_ch = spdk_io_channel_get_ctx(ch);
	struct nvme_qpair *qpair = ctrlr_ch->qpair;
	int rc;

	if (!nvme_qpair_is_connected(qpair)) {
		spdk_for_each_channel_continue(i, 0);
		return;
	}

	rc = spdk_nvme_qpair_authenticate(qpair->qpair, bdev_nvme_authenticate_qpair_done, i);
	if (rc != 0) {
		spdk_for_each_channel_continue(i, rc);
	}
}

static void
bdev_nvme_authenticate_ctrlr_done(void *_ctx, int status)
{
	struct bdev_nvme_set_keys_ctx *ctx = _ctx;

	if (status != 0) {
		bdev_nvme_set_keys_done(ctx, status);
		return;
	}

	spdk_for_each_channel(ctx->nctrlr, bdev_nvme_authenticate_qpair, ctx,
			      bdev_nvme_authenticate_qpairs_done);
}

static void
bdev_nvme_authenticate_ctrlr(struct bdev_nvme_set_keys_ctx *ctx)
{
	struct spdk_nvme_ctrlr_key_opts opts = {};
	struct nvme_ctrlr *nctrlr = ctx->nctrlr;
	int rc;

	opts.size = SPDK_SIZEOF(&opts, dhchap_ctrlr_key);
	opts.dhchap_key = ctx->dhchap_key;
	opts.dhchap_ctrlr_key = ctx->dhchap_ctrlr_key;
	rc = spdk_nvme_ctrlr_set_keys(nctrlr->ctrlr, &opts);
	if (rc != 0) {
		bdev_nvme_set_keys_done(ctx, rc);
		return;
	}

	if (ctx->dhchap_key != NULL) {
		rc = spdk_nvme_ctrlr_authenticate(nctrlr->ctrlr,
						  bdev_nvme_authenticate_ctrlr_done, ctx);
		if (rc != 0) {
			bdev_nvme_set_keys_done(ctx, rc);
		}
	} else {
		bdev_nvme_authenticate_ctrlr_continue(ctx);
	}
}

int
bdev_nvme_set_keys(const char *name, const char *dhchap_key, const char *dhchap_ctrlr_key,
		   bdev_nvme_set_keys_cb cb_fn, void *cb_ctx)
{
	struct bdev_nvme_set_keys_ctx *ctx;
	struct nvme_bdev_ctrlr *nbdev_ctrlr;
	struct nvme_ctrlr *nctrlr;

	ctx = calloc(1, sizeof(*ctx));
	if (ctx == NULL) {
		return -ENOMEM;
	}

	if (dhchap_key != NULL) {
		ctx->dhchap_key = spdk_keyring_get_key(dhchap_key);
		if (ctx->dhchap_key == NULL) {
			SPDK_ERRLOG("Could not find key %s for bdev %s\n", dhchap_key, name);
			bdev_nvme_free_set_keys_ctx(ctx);
			return -ENOKEY;
		}
	}
	if (dhchap_ctrlr_key != NULL) {
		ctx->dhchap_ctrlr_key = spdk_keyring_get_key(dhchap_ctrlr_key);
		if (ctx->dhchap_ctrlr_key == NULL) {
			SPDK_ERRLOG("Could not find key %s for bdev %s\n", dhchap_ctrlr_key, name);
			bdev_nvme_free_set_keys_ctx(ctx);
			return -ENOKEY;
		}
	}

	pthread_mutex_lock(&g_bdev_nvme_mutex);
	nbdev_ctrlr = nvme_bdev_ctrlr_get_by_name(name);
	if (nbdev_ctrlr == NULL) {
		SPDK_ERRLOG("Could not find bdev_ctrlr %s\n", name);
		pthread_mutex_unlock(&g_bdev_nvme_mutex);
		bdev_nvme_free_set_keys_ctx(ctx);
		return -ENODEV;
	}
	assert(!TAILQ_EMPTY(&nbdev_ctrlr->ctrlrs));
	nctrlr = TAILQ_FIRST(&nbdev_ctrlr->ctrlrs);
	nctrlr->ref++;
	pthread_mutex_unlock(&g_bdev_nvme_mutex);

	ctx->nctrlr = nctrlr;
	ctx->cb_fn = cb_fn;
	ctx->cb_ctx = cb_ctx;
	ctx->thread = spdk_get_thread();

	bdev_nvme_authenticate_ctrlr(ctx);

	return 0;
}

void
nvme_io_path_info_json(struct spdk_json_write_ctx *w, struct nvme_io_path *io_path)
{
+5 −0
Original line number Diff line number Diff line
@@ -341,6 +341,11 @@ int bdev_nvme_stop_mdns_discovery(const char *name);
void bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request);
void bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w);

typedef void (*bdev_nvme_set_keys_cb)(void *ctx, int status);

int bdev_nvme_set_keys(const char *name, const char *dhchap_key, const char *dhchap_ctrlr_key,
		       bdev_nvme_set_keys_cb cb_fn, void *cb_ctx);

struct spdk_nvme_ctrlr *bdev_nvme_get_ctrlr(struct spdk_bdev *bdev);

typedef void (*bdev_nvme_delete_done_fn)(void *ctx, int rc);
+54 −0
Original line number Diff line number Diff line
@@ -2741,3 +2741,57 @@ err:
}
SPDK_RPC_REGISTER("bdev_nvme_get_path_iostat", rpc_bdev_nvme_get_path_iostat,
		  SPDK_RPC_RUNTIME)

struct rpc_bdev_nvme_set_keys {
	char *name;
	char *dhchap_key;
	char *dhchap_ctrlr_key;
};

static const struct spdk_json_object_decoder rpc_bdev_nvme_set_keys_decoders[] = {
	{"name", offsetof(struct rpc_bdev_nvme_set_keys, name), spdk_json_decode_string},
	{"dhchap_key", offsetof(struct rpc_bdev_nvme_set_keys, dhchap_key), spdk_json_decode_string, true},
	{"dhchap_ctrlr_key", offsetof(struct rpc_bdev_nvme_set_keys, dhchap_ctrlr_key), spdk_json_decode_string, true},
};

static void
free_rpc_bdev_nvme_set_keys(struct rpc_bdev_nvme_set_keys *req)
{
	free(req->name);
	free(req->dhchap_key);
	free(req->dhchap_ctrlr_key);
}

static void
rpc_bdev_nvme_set_keys_done(void *ctx, int status)
{
	struct spdk_jsonrpc_request *request = ctx;

	if (status != 0) {
		spdk_jsonrpc_send_error_response(request, status, spdk_strerror(-status));
	} else {
		spdk_jsonrpc_send_bool_response(request, true);
	}
}

static void
rpc_bdev_nvme_set_keys(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
	struct rpc_bdev_nvme_set_keys req = {};
	int rc;

	if (spdk_json_decode_object(params, rpc_bdev_nvme_set_keys_decoders,
				    SPDK_COUNTOF(rpc_bdev_nvme_set_keys_decoders), &req)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
						 "spdk_json_decode_object failed");
		return;
	}

	rc = bdev_nvme_set_keys(req.name, req.dhchap_key, req.dhchap_ctrlr_key,
				rpc_bdev_nvme_set_keys_done, request);
	if (rc != 0) {
		spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
	}
	free_rpc_bdev_nvme_set_keys(&req);
}
SPDK_RPC_REGISTER("bdev_nvme_set_keys", rpc_bdev_nvme_set_keys, SPDK_RPC_RUNTIME)
+15 −0
Original line number Diff line number Diff line
@@ -1024,6 +1024,21 @@ def bdev_nvme_cuse_unregister(client, name):
    return client.call('bdev_nvme_cuse_unregister', params)


def bdev_nvme_set_keys(client, name, dhchap_key=None, dhchap_ctrlr_key=None):
    """Set DH-HMAC-CHAP keys and force (re)authentication on all connected qpairs.
    Args:
        name: name of the NVMe controller
        dhchap_key: DH-HMAC-CHAP key name
        dhchap_ctrlr_key: DH-HMAC-CHAP controller key name
    """
    params = {'name': name}
    if dhchap_key is not None:
        params['dhchap_key'] = dhchap_key
    if dhchap_ctrlr_key is not None:
        params['dhchap_ctrlr_key'] = dhchap_ctrlr_key
    return client.call('bdev_nvme_set_keys', params)


def bdev_zone_block_create(client, name, base_bdev, zone_capacity, optimal_open_zones):
    """Creates a virtual zone device on top of existing non-zoned bdev.
    Args:
Loading