Skip to content

libnvme/tid: numeric-only, sanitizing Transport ID (TID)#3554

Merged
igaw merged 7 commits into
linux-nvme:masterfrom
martin-belanger:libnvme-smart-tid
Jul 7, 2026
Merged

libnvme/tid: numeric-only, sanitizing Transport ID (TID)#3554
igaw merged 7 commits into
linux-nvme:masterfrom
martin-belanger:libnvme-smart-tid

Conversation

@martin-belanger

Copy link
Copy Markdown

libnvme/tid: numeric-only, sanitizing Transport ID (TID)

Cleanup/prep for the upcoming INI connection-config parser, not a feature. That parser needs a TID that's a pure function of numeric input. Removes get_hash()/equal() (upstream API, zero production callers) and formalizes numeric-only, sanitizing construction. A hostname member with deferred resolution was prototyped while designing the config parser but never merged upstream -- rejected, see TID.md.

Follow-up (not in this PR): drop hostname2traddr(), the last real getaddrinfo() caller in libnvme (the other 3 call sites use AI_NUMERICHOST, no DNS). Push resolution to CLI/discoverd, drop NVME_HAVE_NETDB/<netdb.h> entirely. libnvmf_traddr_is_numeric() (added here) is what that follow-up will use.

Commits (review order)

  1. design/INTEGRATION.md -- library/app boundary: libnvme owns protocol I/O, caller owns policy/non-determinism/concurrency.
  2. design/TID.md -- design authority for the TID.
  3. tid: reject hostnames, canonicalize numeric addresses -- traddr/host_traddr canonicalized via inet_pton/inet_ntop; hostname fails construction. New libnvmf_traddr_is_numeric().
  4. tid: drop get_hash() and equal() -- dead public API; hashing moves to the one caller that needs it (discoverd, via get_canonical()).
  5. tid: replace per-field setters with a set_identity triplet -- addressing construction-only; set_identity() owns host-identity rules.
  6. fabrics: use str(), not get_canonical(), in the exclusion log -- fixes a log line that could exceed 500 chars.
  7. tid: group functions by role -- pure reorder, no content change.

Numbers

  • Code: +409/-328 | Tests: +171/-101 | Docs: +170
  • meson test: 81/0 fail | checkpatch + valgrind clean

Comment thread libnvme/design/INTEGRATION.md Outdated
Comment thread libnvme/design/INTEGRATION.md Outdated
Comment thread libnvme/design/TID.md Outdated
Comment thread libnvme/design/TID.md Outdated
Comment thread libnvme/src/nvme/tid.c Outdated
Comment thread libnvme/src/nvme/tid.h Outdated
Comment thread libnvme/src/nvme/tid.c
@igaw

igaw commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Looks all fine, my biggest concern is the amount of documentation which is bloaty/repetitive and sometimes too specific (e.g. time references, change logs,...). It overloads not just me, I got feedback that people are just not reading the lengthy commit messages and documentation. While it is nice to document the code base, we also have to keep it balanced, it is not just for the machines.

@martin-belanger

Copy link
Copy Markdown
Author

Looks all fine, my biggest concern is the amount of documentation which is bloaty/repetitive and sometimes too specific (e.g. time references, change logs,...). It overloads not just me, I got feedback that people are just not reading the lengthy commit messages and documentation. While it is nice to document the code base, we also have to keep it balanced, it is not just for the machines.

Agreed, I'll trim the docs.

@martin-belanger

Copy link
Copy Markdown
Author

@igaw - trimmed the repeated rationale in INTEGRATION.md, TID.md, and the matching kdoc. Reworded the hostid "never random" claim. Docs are now roughly 40% of their original length.

Comment thread libnvme/design/INTEGRATION.md Outdated
Comment thread libnvme/design/INTEGRATION.md Outdated
@igaw

igaw commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Looks good, just my usual nitpicking.

and the reduction/cleanup in the docs is way better, thanks a lot!

Martin Belanger added 7 commits July 7, 2026 07:52
Add INTEGRATION.md, a design doc explaining where the line falls
between libnvme and the application that uses it: libnvme owns NVMe
protocol I/O, the caller owns policy, non-determinism, and its own
concurrency. It gives the main-loop contract (offload every blocking
call via a worker thread or a subprocess/unit) and works the hostname-
resolution example through the boundary.

The CLI/library split never came with a stated rule for what belongs on
each side, which is how resolution ended up inside the library. This is
that rule, so future decisions have a lens to be judged against.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
Add TID.md, the design authority for the NVMe-oF Transport ID: what a
TID carries, how the constructors sanitize numeric addressing, why a
hostname is rejected rather than resolved or deferred (addressing must
be a pure function of numeric inputs -- see INTEGRATION.md), how
subsysnqn/hostnqn/hostid are layered on afterward, and why the
canonical form it produces is not a connection-equality oracle.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
Sanitize traddr/host_traddr on the IP transports (tcp, rdma) at
construction: canonicalize a numeric address (inet_pton/inet_ntop, so a
compressed vs. expanded IPv6 or an IPv4-mapped form collapses to one
spelling) and reject a hostname outright. There is no deferred or
pending state -- from_fields()/parse() simply fail.

Resolving a hostname is the caller's job, not libnvme's: a name can
resolve to several addresses, and choosing one is a policy decision
libnvme has no basis to make, on top of which the lookup can block on
DNS (see design/INTEGRATION.md). libnvmf_traddr_is_numeric() lets a
caller check a candidate address, using the same definition of numeric
the constructors enforce, before deciding whether it needs to resolve.

dup() keeps its verbatim xstrdup() copy rather than routing through
from_fields(): the source is already sanitized, so re-running it through
canon_ip() would only re-canonicalize and re-allocate addresses that are
already in canonical form.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
Both are pre-existing public API with no production caller in the tree
(only their own unit tests). get_hash() derives a 48-bit FNV-1a name
from the canonical string -- an algorithm, width, and format that are
naming policy for whichever consumer needs it (discoverd, for its
systemd unit names), not something libnvme has a basis to own. That
consumer computes its own hash from libnvmf_tid_get_canonical() instead.

equal() compares all eight fields with exact string equality, which
diverges from the two comparators the tree already has for a reason:
libnvme_ipaddrs_eq()/addr_equal() (address-aware) and the kernel-aware
sysfs connection matcher. A third, byte-exact equality with no caller
is more likely to be reached for by mistake than to be missed.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
Addressing is now construction-only (from_fields/parse/dup, all
sanitizing), so the eight per-field setters are removed and the fields
marked write=none: there is no longer any way to set an address that
bypasses sanitization, and transport -- the root of a TID's identity --
is immutable.

The subsystem and host identity are layered on afterward through one
call, libnvmf_tid_set_identity(subsysnqn, hostnqn, hostid), where NULL
leaves a field unchanged. It is also where the host identity rules live:
a missing hostid is derived from a UUID-format hostnqn (TP4126) so it is
deterministic and never random, and a hostid without a hostnqn is
rejected -- one host is one (hostnqn, hostid) pair.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
The exclusion-skip INFO message rendered the TID with get_canonical(),
which includes the full 8-field identity. subsysnqn and hostnqn can each
run to 223 characters, so that single log line could exceed 500
characters. get_canonical() is for byte-stable identity (matching for
exclusion, and eventually hashing); str() is the compact, human-scannable
rendering meant for exactly this kind of log line.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
Reorder with no content change: constructors first (from_fields, its
companion set_identity, dup, parse, parse_strict), then the
construction-adjacent traddr_is_numeric, then predicates and derived
views on an existing TID (is_empty, get_canonical, str, validate).

set_identity immediately follows from_fields because from_fields alone
produces an incomplete TID -- set_identity is what completes it.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
@igaw
igaw merged commit fd1c5a0 into linux-nvme:master Jul 7, 2026
31 of 32 checks passed
@igaw

igaw commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Thanks a lot!

@martin-belanger
martin-belanger deleted the libnvme-smart-tid branch July 15, 2026 11:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants