diff --git a/libnvme/design/EXCLUSIONS.md b/libnvme/design/EXCLUSIONS.md index c1e10e9cde..046714bce4 100644 --- a/libnvme/design/EXCLUSIONS.md +++ b/libnvme/design/EXCLUSIONS.md @@ -96,6 +96,8 @@ if (libnvmf_exclusion_match(ctx, tid)) `libnvmf_exclusion_match()` re-reads the files on every call (no caching), so an edit takes effect on the next connection attempt without restarting anything. This is the only API an orchestrator needs; the create/add/remove calls are for the management tooling. +`nvme connect-all` is itself such an orchestrating path, and libnvme's discovery-connect code consults the list on its behalf: before connecting anything it *enumerated* — a Discovery Log Page entry (including referral DCs), an NBFT record, or a controller listed in `discovery.conf` / `config.json` — it checks for a match and skips with an INFO-level "skipping excluded controller" note. A Discovery Controller named explicitly on the command line (`nvme discover` / `connect-all` with an address) is a targeted human action and is not checked — though the entries *it* enumerates still are. + The match is by design *not* allowed to **block** `nvme connect ` or `nvme disconnect `: those are single, targeted human actions where the operator's intent is explicit. The list governs the *orchestrating* paths that decide on their own. As a courtesy, `nvme connect --verbose` does *consult* the list and prints a note when the target matches — a heads-up that you are overriding your own opt-out — but it still connects. ## Managing the list diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 01c858943e..ada35d05d0 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -1538,6 +1538,50 @@ static void nvmf_update_tls_concat(struct nvmf_disc_log_entry *e, } } +/* + * Enumerated-connect gate: consult the exclusion list before connecting a + * controller that was enumerated from a Discovery Log Page, the NBFT table, + * or a configuration file. Controllers named explicitly by the user + * ("nvme connect", "nvme discover" with an address) are deliberately not + * checked -- a targeted human action overrides the list. + */ +static bool nvmf_excluded(struct libnvme_global_ctx *ctx, + const char *transport, const char *traddr, + const char *trsvcid, const char *subsysnqn, + const char *host_traddr, const char *host_iface, + const char *hostnqn, const char *hostid) +{ + struct libnvmf_tid *tid; + const char *canonical; + bool excluded; + + tid = libnvmf_tid_from_fields(transport, traddr, trsvcid, subsysnqn, + host_traddr, host_iface, hostnqn, + hostid); + if (!tid) + return false; /* fail-safe: never block on allocation failure */ + + excluded = libnvmf_exclusion_match(ctx, tid); + if (excluded) { + canonical = libnvmf_tid_get_canonical(tid); + libnvme_msg(ctx, LIBNVME_LOG_INFO, + "skipping excluded controller %s\n", + canonical ? canonical : subsysnqn); + } + libnvmf_tid_free(tid); + + return excluded; +} + +static bool nvmf_ctrl_excluded(struct libnvme_global_ctx *ctx, + struct libnvme_host *h, struct libnvme_ctrl *c) +{ + return nvmf_excluded(ctx, c->transport, c->traddr, c->trsvcid, + c->subsysnqn, c->host_traddr, c->host_iface, + libnvme_host_get_hostnqn(h), + libnvme_host_get_hostid(h)); +} + static int nvmf_connect_disc_entry(libnvme_host_t h, struct nvmf_disc_log_entry *e, struct libnvmf_context *fctx, @@ -1591,6 +1635,15 @@ static int nvmf_connect_disc_entry(libnvme_host_t h, fctx->ctrl_params.transport, fctx->ctrl_params.traddr, fctx->ctrl_params.trsvcid); + if (nvmf_excluded(h->ctx, fctx->ctrl_params.transport, + fctx->ctrl_params.traddr, fctx->ctrl_params.trsvcid, + fctx->ctrl_params.subsysnqn, + fctx->ctrl_params.host_traddr, + fctx->ctrl_params.host_iface, + libnvme_host_get_hostnqn(h), + libnvme_host_get_hostid(h))) + return -EPERM; + ret = libnvme_create_ctrl(h->ctx, &fctx->ctrl_params, &c); if (ret) { libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, "skipping discovery entry, " @@ -2642,6 +2695,15 @@ int _discovery_config_json(struct libnvme_global_ctx *ctx, if (libnvme_ctrl_get_persistent(c)) nfctx.persistent = true; + if (nvmf_excluded(ctx, nfctx.ctrl_params.transport, + nfctx.ctrl_params.traddr, nfctx.ctrl_params.trsvcid, + nfctx.ctrl_params.subsysnqn, + nfctx.ctrl_params.host_traddr, + nfctx.ctrl_params.host_iface, + libnvme_host_get_hostnqn(h), + libnvme_host_get_hostid(h))) + return 0; + if (!force) { cn = lookup_ctrl(h, &nfctx); if (cn) { @@ -2750,6 +2812,9 @@ __libnvme_public int libnvmf_connect_config_json(struct libnvme_global_ctx *ctx, strcmp(transport, "fc")) continue; + if (nvmf_ctrl_excluded(ctx, h, c)) + continue; + err = libnvmf_connect_ctrl(c); if (err) { if (err == -ENVME_CONNECT_ALREADY) @@ -2786,6 +2851,14 @@ __libnvme_public int libnvmf_discovery_config_file( err = fctx->hooks.parser_next_line(&nfctx, fctx->hooks.user_data); if (err) break; + if (nvmf_excluded(ctx, nfctx.ctrl_params.transport, + nfctx.ctrl_params.traddr, + nfctx.ctrl_params.trsvcid, + nfctx.ctrl_params.subsysnqn, + nfctx.ctrl_params.host_traddr, + nfctx.ctrl_params.host_iface, + nfctx.hostnqn, nfctx.hostid)) + continue; libnvmf_discovery(ctx, &nfctx, connect, force); } while (!err); @@ -2951,6 +3024,15 @@ static int nbft_connect(struct libnvme_global_ctx *ctx, if (c && libnvme_ctrl_get_name(c)) return 0; + if (nvmf_excluded(ctx, fctx->ctrl_params.transport, + fctx->ctrl_params.traddr, fctx->ctrl_params.trsvcid, + fctx->ctrl_params.subsysnqn, + fctx->ctrl_params.host_traddr, + fctx->ctrl_params.host_iface, + libnvme_host_get_hostnqn(h), + libnvme_host_get_hostid(h))) + return 0; + ret = libnvme_create_ctrl(ctx, &fctx->ctrl_params, &c); if (ret) return ret;