* [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles
@ 2026-05-26 14:40 Benjamin Coddington
2026-05-26 14:40 ` [PATCH 1/1] nfs-fh-verify: add tool to validate kNFSD filehandle signatures Benjamin Coddington
2026-05-26 15:37 ` [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles Chuck Lever
0 siblings, 2 replies; 4+ messages in thread
From: Benjamin Coddington @ 2026-05-26 14:40 UTC (permalink / raw)
To: Steve Dickson; +Cc: linux-nfs, Chuck Lever, Jeff Layton
We've had a lot of folks configure signed filehandles, and then wonder "Is
this thing actually working?". Given the various filehandle lengths and
types, it can be pretty difficult to know if the kernel is actually
correctly signing its filehandles. Claude and I wrote this tool that allows
anyone to verify the correct functioning of filehandle signing given the
original key and a list of filehandles.
Beyond that, this tool shows a way to monitor the configured state of signed
filehandles in a distributed installation.
Benjamin Coddington (1):
nfs-fh-verify: add tool to validate kNFSD filehandle signatures
configure.ac | 7 +
tools/Makefile.am | 4 +
tools/nfs-fh-verify/Makefile.am | 10 +
tools/nfs-fh-verify/main.c | 337 ++++++++++++++++++++++++++
tools/nfs-fh-verify/nfs-fh-verify.man | 171 +++++++++++++
5 files changed, 529 insertions(+)
create mode 100644 tools/nfs-fh-verify/Makefile.am
create mode 100644 tools/nfs-fh-verify/main.c
create mode 100644 tools/nfs-fh-verify/nfs-fh-verify.man
base-commit: a806c9d65662ecf5d40c00d60a514e13ada8d76e
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/1] nfs-fh-verify: add tool to validate kNFSD filehandle signatures
2026-05-26 14:40 [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles Benjamin Coddington
@ 2026-05-26 14:40 ` Benjamin Coddington
2026-05-26 15:37 ` [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles Chuck Lever
1 sibling, 0 replies; 4+ messages in thread
From: Benjamin Coddington @ 2026-05-26 14:40 UTC (permalink / raw)
To: Steve Dickson; +Cc: linux-nfs, Chuck Lever, Jeff Layton
knfsd optionally signs filehandles with an 8-byte SipHash-2-4 MAC for
exports marked with the sign_fh option (see fs/nfsd/nfsfh.c, kernel
commit 2a83ffc55750). When triaging captures or filehandle issues it is
useful to confirm offline that a given filehandle's MAC verifies under
the same key the running server was configured with.
Add a small standalone utility, nfs-fh-verify, that:
- reads filehandles in ASCII hex from stdin (one per line, tolerant of
"0x" prefixes, ':' or whitespace separators, '#' comments, and
mixed case);
- derives the 128-bit SipHash key from a key file using the existing
hash_fh_key_file() helper, matching what nfsdctl pushes to the
kernel via NFSD_A_SERVER_FH_KEY;
- recomputes the trailing 8-byte MAC over fh[0..n-8] and reports
OK, BAD, or MALFORMED for each filehandle;
- exits 0 if every parsed filehandle verified, 1 on any BAD/MALFORMED,
or 2 on setup error.
The SipHash-2-4 implementation is embedded as a small reference port of
the kernel's lib/siphash.c, validated at startup against two published
Aumasson/Bernstein test vectors so that an incorrect build refuses to
emit verdicts rather than silently mis-reporting.
Wired in behind --disable-nfs-fh-verify (default on), mirroring the
existing nfsrahead pattern.
Signed-off-by: Benjamin Coddington <bcodding@hammerspace.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
configure.ac | 7 +
tools/Makefile.am | 4 +
tools/nfs-fh-verify/Makefile.am | 10 +
tools/nfs-fh-verify/main.c | 337 ++++++++++++++++++++++++++
tools/nfs-fh-verify/nfs-fh-verify.man | 171 +++++++++++++
5 files changed, 529 insertions(+)
create mode 100644 tools/nfs-fh-verify/Makefile.am
create mode 100644 tools/nfs-fh-verify/main.c
create mode 100644 tools/nfs-fh-verify/nfs-fh-verify.man
diff --git a/configure.ac b/configure.ac
index 8ca06fd62b47..1fc8f8cb9552 100644
--- a/configure.ac
+++ b/configure.ac
@@ -247,6 +247,12 @@ AC_ARG_ENABLE(nfsrahead,
PKG_CHECK_MODULES([LIBMOUNT], [mount])
fi
+AC_ARG_ENABLE(nfs-fh-verify,
+ [AS_HELP_STRING([--disable-nfs-fh-verify],[disable nfs-fh-verify command @<:@default=no@:>@])],
+ enable_nfs_fh_verify=$enableval,
+ enable_nfs_fh_verify="yes")
+ AM_CONDITIONAL(CONFIG_NFS_FH_VERIFY, [test "$enable_nfs_fh_verify" = "yes" ])
+
AC_ARG_ENABLE(nfsdcltrack,
[AS_HELP_STRING([--enable-nfsdcltrack],[enable NFSv4 clientid tracking programs @<:@default=no@:>@])],
enable_nfsdcltrack=$enableval,
@@ -753,6 +759,7 @@ AC_CONFIG_FILES([
tools/mountstats/Makefile
tools/nfs-iostat/Makefile
tools/nfsrahead/Makefile
+ tools/nfs-fh-verify/Makefile
tools/rpcctl/Makefile
tools/nfsdclnts/Makefile
tools/nfsconf/Makefile
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 48fd0cdf1f83..4478b3cfd664 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -16,6 +16,10 @@ if CONFIG_NFSRAHEAD
OPTDIRS += nfsrahead
endif
+if CONFIG_NFS_FH_VERIFY
+OPTDIRS += nfs-fh-verify
+endif
+
SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS)
MAINTAINERCLEANFILES = Makefile.in
diff --git a/tools/nfs-fh-verify/Makefile.am b/tools/nfs-fh-verify/Makefile.am
new file mode 100644
index 000000000000..6403d9b9cab7
--- /dev/null
+++ b/tools/nfs-fh-verify/Makefile.am
@@ -0,0 +1,10 @@
+## Process this file with automake to produce Makefile.in
+
+sbin_PROGRAMS = nfs-fh-verify
+nfs_fh_verify_SOURCES = main.c
+nfs_fh_verify_LDADD = ../../support/nfs/libnfs.la
+
+man8_MANS = nfs-fh-verify.man
+EXTRA_DIST = $(man8_MANS)
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/tools/nfs-fh-verify/main.c b/tools/nfs-fh-verify/main.c
new file mode 100644
index 000000000000..389d1765ba60
--- /dev/null
+++ b/tools/nfs-fh-verify/main.c
@@ -0,0 +1,337 @@
+/*
+ * nfs-fh-verify - Verify NFS filehandle signatures produced by knfsd.
+ *
+ * Reads filehandles in ASCII hex from stdin (one per line) and, using a
+ * filehandle signing key file, reports whether the trailing 8-byte
+ * SipHash-2-4 MAC verifies under that key.
+ *
+ * Mirrors the kernel signing layout in fs/nfsd/nfsfh.c (commit 2a83ffc55750)
+ * and the key derivation in support/nfs/fh_key_file.c.
+ */
+#include "config.h"
+
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <uuid/uuid.h>
+
+#include "nfslib.h"
+#include "xlog.h"
+
+#define FH_MAC_LEN 8
+#define FH_MAX_LEN 128 /* NFSv3=64, NFSv4=128 */
+
+/*
+ * SipHash-2-4. Matches lib/siphash.c in the kernel byte-for-byte:
+ * 2 compression rounds per 64-bit block, 4 finalization rounds, length
+ * in the high byte of the final word, output v0^v1^v2^v3.
+ */
+static inline uint64_t rotl64(uint64_t x, int b)
+{
+ return (x << b) | (x >> (64 - b));
+}
+
+#define SIPROUND do { \
+ v0 += v1; v1 = rotl64(v1, 13); v1 ^= v0; v0 = rotl64(v0, 32); \
+ v2 += v3; v3 = rotl64(v3, 16); v3 ^= v2; \
+ v0 += v3; v3 = rotl64(v3, 21); v3 ^= v0; \
+ v2 += v1; v1 = rotl64(v1, 17); v1 ^= v2; v2 = rotl64(v2, 32); \
+} while (0)
+
+static uint64_t load_le64(const uint8_t *p)
+{
+ return (uint64_t)p[0] | ((uint64_t)p[1] << 8) |
+ ((uint64_t)p[2] << 16) | ((uint64_t)p[3] << 24) |
+ ((uint64_t)p[4] << 32) | ((uint64_t)p[5] << 40) |
+ ((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56);
+}
+
+static uint64_t siphash24(const uint8_t *data, size_t len,
+ uint64_t k0, uint64_t k1)
+{
+ uint64_t v0 = 0x736f6d6570736575ULL ^ k0;
+ uint64_t v1 = 0x646f72616e646f6dULL ^ k1;
+ uint64_t v2 = 0x6c7967656e657261ULL ^ k0;
+ uint64_t v3 = 0x7465646279746573ULL ^ k1;
+ uint64_t b = ((uint64_t)len) << 56;
+ const uint8_t *end = data + (len & ~(size_t)7);
+ size_t left = len & 7;
+ uint64_t m;
+
+ for (; data != end; data += 8) {
+ m = load_le64(data);
+ v3 ^= m;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= m;
+ }
+
+ switch (left) {
+ case 7: b |= ((uint64_t)data[6]) << 48; /* fallthrough */
+ case 6: b |= ((uint64_t)data[5]) << 40; /* fallthrough */
+ case 5: b |= ((uint64_t)data[4]) << 32; /* fallthrough */
+ case 4: b |= ((uint64_t)data[3]) << 24; /* fallthrough */
+ case 3: b |= ((uint64_t)data[2]) << 16; /* fallthrough */
+ case 2: b |= ((uint64_t)data[1]) << 8; /* fallthrough */
+ case 1: b |= ((uint64_t)data[0]);
+ break;
+ case 0:
+ break;
+ }
+
+ v3 ^= b;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= b;
+ v2 ^= 0xff;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ return v0 ^ v1 ^ v2 ^ v3;
+}
+
+/*
+ * Two well-known SipHash-2-4 reference vectors (Aumasson/Bernstein) with
+ * key = bytes 0x00..0x0f. Verifies the embedded implementation at startup;
+ * if this fails we refuse to run rather than emit wrong verdicts.
+ */
+static int siphash_self_test(void)
+{
+ static const uint8_t key[16] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ };
+ static const uint8_t msg15[15] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ };
+ uint64_t k0 = load_le64(key);
+ uint64_t k1 = load_le64(key + 8);
+ uint64_t got;
+
+ got = siphash24(NULL, 0, k0, k1);
+ if (got != 0x726fdb47dd0e0e31ULL) {
+ fprintf(stderr,
+ "nfs-fh-verify: siphash self-test failed (len=0): %016lx\n",
+ (unsigned long)got);
+ return -1;
+ }
+ got = siphash24(msg15, sizeof(msg15), k0, k1);
+ if (got != 0xa129ca6149be45e5ULL) {
+ fprintf(stderr,
+ "nfs-fh-verify: siphash self-test failed (len=15): %016lx\n",
+ (unsigned long)got);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Parse one filehandle line into a byte buffer.
+ *
+ * Accepts: optional "0x" prefix; hex pairs optionally separated by ':' or
+ * whitespace; '#' starts a comment; blank lines yield len=0.
+ *
+ * Returns 0 on success and sets *len, or -1 with errno set on bad input.
+ */
+static int parse_hex_fh(const char *line, uint8_t *out, size_t max,
+ size_t *len)
+{
+ const char *p = line;
+ size_t n = 0;
+ int hi = -1;
+
+ while (*p && isspace((unsigned char)*p))
+ p++;
+ if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
+ p += 2;
+
+ for (; *p; p++) {
+ unsigned char c = (unsigned char)*p;
+ int v;
+
+ if (c == '#' || c == '\n' || c == '\r')
+ break;
+ if (isspace(c) || c == ':')
+ continue;
+
+ if (c >= '0' && c <= '9')
+ v = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ v = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ v = c - 'A' + 10;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (hi < 0) {
+ hi = v;
+ } else {
+ if (n >= max) {
+ errno = E2BIG;
+ return -1;
+ }
+ out[n++] = (uint8_t)((hi << 4) | v);
+ hi = -1;
+ }
+ }
+
+ if (hi >= 0) {
+ errno = EINVAL; /* odd nibble count */
+ return -1;
+ }
+ *len = n;
+ return 0;
+}
+
+static void print_hex(FILE *f, const uint8_t *buf, size_t len)
+{
+ for (size_t i = 0; i < len; i++)
+ fprintf(f, "%02x", buf[i]);
+}
+
+static void usage(FILE *f, const char *prog)
+{
+ fprintf(f,
+"Usage: %s -k <fh-key-file> [-q] [-v]\n"
+"\n"
+" Reads NFS filehandles in ASCII hex from stdin, one per line, and\n"
+" reports whether each filehandle's trailing 8-byte SipHash-2-4 MAC\n"
+" verifies under the key derived from <fh-key-file>.\n"
+"\n"
+"Options:\n"
+" -k FILE Filehandle signing key file (same file passed to nfsdctl).\n"
+" -q Quiet: print just OK/BAD/MALFORMED per line.\n"
+" -v Verbose: also print computed MAC and trailing MAC bytes.\n"
+" -h Show this help.\n"
+"\n"
+"Input format:\n"
+" Hex chars per filehandle, one filehandle per line. Optional \"0x\"\n"
+" prefix; ':' and whitespace between hex pairs ignored; '#' starts a\n"
+" comment; blank lines are skipped.\n"
+"\n"
+"Exit status:\n"
+" 0 every parsed filehandle verified OK\n"
+" 1 any BAD or MALFORMED verdict\n"
+" 2 setup error (key file unreadable, bad usage, etc.)\n",
+ prog);
+}
+
+int main(int argc, char **argv)
+{
+ const char *keyfile = NULL;
+ bool quiet = false;
+ bool verbose = false;
+ uuid_t key_uuid;
+ uint64_t k0, k1;
+ char *line = NULL;
+ size_t cap = 0;
+ ssize_t nread;
+ int rc, opt;
+ int saw_bad = 0;
+
+ xlog_stderr(1);
+ xlog_syslog(0);
+ xlog_open("nfs-fh-verify");
+
+ while ((opt = getopt(argc, argv, "k:qvh")) != -1) {
+ switch (opt) {
+ case 'k': keyfile = optarg; break;
+ case 'q': quiet = true; break;
+ case 'v': verbose = true; break;
+ case 'h': usage(stdout, argv[0]); return 0;
+ default: usage(stderr, argv[0]); return 2;
+ }
+ }
+ if (!keyfile) {
+ fprintf(stderr, "nfs-fh-verify: -k <fh-key-file> is required\n");
+ usage(stderr, argv[0]);
+ return 2;
+ }
+ if (optind != argc) {
+ fprintf(stderr, "nfs-fh-verify: unexpected positional argument\n");
+ return 2;
+ }
+
+ if (siphash_self_test() != 0)
+ return 2;
+
+ rc = hash_fh_key_file(keyfile, key_uuid);
+ if (rc != 0) {
+ /* hash_fh_key_file() already logged via xlog */
+ return 2;
+ }
+
+ k0 = load_le64(key_uuid);
+ k1 = load_le64(key_uuid + 8);
+
+ if (verbose) {
+ char ustr[37];
+ uuid_unparse(key_uuid, ustr);
+ fprintf(stderr, "key uuid: %s k0=%016lx k1=%016lx\n",
+ ustr, (unsigned long)k0, (unsigned long)k1);
+ }
+
+ while ((nread = getline(&line, &cap, stdin)) != -1) {
+ uint8_t fh[FH_MAX_LEN];
+ size_t fh_len = 0;
+ const char *verdict;
+ uint64_t mac, trailing;
+
+ if (parse_hex_fh(line, fh, sizeof(fh), &fh_len) != 0) {
+ saw_bad = 1;
+ if (!quiet)
+ fputs(line, stdout);
+ printf("MALFORMED (%s)\n",
+ errno == E2BIG ? "filehandle too long"
+ : "bad hex");
+ continue;
+ }
+ if (fh_len == 0)
+ continue; /* blank/comment line */
+ if (fh_len <= FH_MAC_LEN) {
+ saw_bad = 1;
+ if (!quiet) {
+ print_hex(stdout, fh, fh_len);
+ putchar(' ');
+ }
+ printf("MALFORMED (too short for MAC)\n");
+ continue;
+ }
+
+ mac = siphash24(fh, fh_len - FH_MAC_LEN, k0, k1);
+ trailing = load_le64(fh + fh_len - FH_MAC_LEN);
+ verdict = (mac == trailing) ? "OK" : "BAD";
+ if (verdict[0] == 'B')
+ saw_bad = 1;
+
+ if (!quiet) {
+ print_hex(stdout, fh, fh_len);
+ putchar(' ');
+ }
+ fputs(verdict, stdout);
+ if (verbose)
+ printf(" expected=%016lx got=%016lx",
+ (unsigned long)mac, (unsigned long)trailing);
+ putchar('\n');
+ }
+
+ free(line);
+ if (ferror(stdin)) {
+ fprintf(stderr, "nfs-fh-verify: error reading stdin: %s\n",
+ strerror(errno));
+ return 2;
+ }
+ return saw_bad ? 1 : 0;
+}
diff --git a/tools/nfs-fh-verify/nfs-fh-verify.man b/tools/nfs-fh-verify/nfs-fh-verify.man
new file mode 100644
index 000000000000..dc3594cafd17
--- /dev/null
+++ b/tools/nfs-fh-verify/nfs-fh-verify.man
@@ -0,0 +1,171 @@
+.\" Manpage for nfs-fh-verify.
+.nh
+.ad l
+.TH NFS-FH-VERIFY 8 "20 May 2026" "nfs-utils" "System Manager's Manual"
+.SH NAME
+nfs-fh-verify \- verify NFS filehandle signatures produced by knfsd
+
+.SH SYNOPSIS
+.B nfs-fh-verify
+.B \-k
+.I fh-key-file
+.RB [ \-q ]
+.RB [ \-v ]
+
+.SH DESCRIPTION
+.B nfs-fh-verify
+reads NFS filehandles in ASCII hex from standard input, one filehandle per
+line, and reports whether each filehandle's trailing 8-byte SipHash-2-4
+Message Authentication Code (MAC) verifies under the key derived from
+.IR fh-key-file .
+It is intended for offline analysis of filehandles captured from network
+traces, for example with tcpdump(8) or tshark(1).
+
+The kernel NFS server (knfsd) optionally appends a SipHash-2-4 MAC to
+filehandles for exports marked with the
+.B sign_fh
+export option. The MAC is keyed by a 128-bit secret derived from a key
+file passed to the server via
+.BR nfsdctl (8).
+This tool reproduces that key derivation and MAC computation so that
+filehandles can be validated without involving the running server.
+
+.SH OPTIONS
+.TP
+.BI \-k " fh-key-file"
+Path to the filehandle signing key file. This is the same file passed to
+.B nfsdctl
+and configured via the
+.B fh-key-file
+setting in
+.BR nfsd (7).
+The file's contents are hashed (SHA-1) into a 16-byte key that is then
+interpreted as a SipHash-2-4 key.
+.TP
+.B \-q
+Quiet mode: print only the per-filehandle verdict
+.RB ( OK ", " BAD ", or " MALFORMED ),
+suppressing the input hex.
+.TP
+.B \-v
+Verbose mode: also print the computed MAC and the trailing 8 MAC bytes
+extracted from each filehandle, in hex. Useful when debugging captures or
+disagreements between client and server.
+.TP
+.B \-h
+Show usage and exit.
+
+.SH INPUT FORMAT
+One filehandle per line. Each line may contain:
+.IP \(bu 2
+An optional
+.B 0x
+prefix.
+.IP \(bu 2
+Hex digits in upper or lower case.
+.IP \(bu 2
+.B ":"
+or whitespace between byte pairs (ignored).
+.IP \(bu 2
+A
+.B "#"
+to start a trailing comment.
+.PP
+Blank lines and comment-only lines are skipped. An odd number of hex
+nibbles, a non-hex character, or a filehandle longer than the NFSv4
+maximum (128 bytes) is reported as
+.BR MALFORMED .
+
+.SH VERDICTS
+.TP
+.B OK
+The last 8 bytes of the filehandle match
+.BR siphash24 ( fh [0..n\-8] ", " key ).
+.TP
+.B BAD
+The MAC does not verify. The filehandle was either signed with a
+different key, tampered with, or never signed at all (note: signing is
+per-export, so an unsigned filehandle from an export without
+.B sign_fh
+will also appear as
+.BR BAD ).
+.TP
+.B MALFORMED
+The line could not be parsed as a hex filehandle, or was too short to
+contain an 8-byte MAC.
+
+.SH EXIT STATUS
+.TP
+.B 0
+Every parsed filehandle verified
+.BR OK .
+.TP
+.B 1
+At least one filehandle was
+.B BAD
+or
+.BR MALFORMED .
+.TP
+.B 2
+Setup error: bad usage, key file unreadable, internal self-test failure.
+
+.SH EXAMPLES
+Watch live traffic on the loopback interface and verify every filehandle
+as it goes by \(em a quick way to confirm that an export is actually
+signing filehandles:
+.PP
+.RS 4
+.nf
+$ tshark \-i lo \-T fields \-E separator=\\n \-e nfs.fhandle \\
+ | nfs\-fh\-verify \-k /etc/nfs_fh.key
+.fi
+.RE
+.PP
+Capturing on the loopback interface
+.RB ( "\-i lo" )
+only sees NFS traffic when the server has mounted one of its own exports,
+so that client and server are the same host. To check filehandles handed
+out to a remote client, capture on the interface that carries the NFS
+traffic instead (for example
+.BR "\-i eth0" ).
+.PP
+The
+.B nfs.fhandle
+field carries the raw filehandle for both NFSv3 and NFSv4, and
+.B "\-E separator=\en"
+puts each filehandle on its own line so that responses carrying more than
+one filehandle are checked individually.
+.PP
+Verify filehandles extracted from a saved capture:
+.PP
+.RS 4
+.nf
+$ tshark \-r capture.pcap \-Y nfs \-T fields \-E separator=\\n \\
+ \-e nfs.fhandle | nfs\-fh\-verify \-k /etc/nfs_fh.key
+.fi
+.RE
+.PP
+Summarize the verdicts over a large capture:
+.PP
+.RS 4
+.nf
+$ tshark \-r capture.pcap \-T fields \-E separator=\\n \-e nfs.fhandle \\
+ | nfs\-fh\-verify \-q \-k /etc/nfs_fh.key | sort | uniq \-c
+.fi
+.RE
+.PP
+Inline check of one filehandle with debug output:
+.PP
+.RS 4
+.nf
+$ echo 0x01000601...deadbeef | nfs\-fh\-verify \-v \-k /etc/nfs_fh.key
+.fi
+.RE
+
+.SH SEE ALSO
+.BR nfsdctl (8),
+.BR exportfs (8),
+.BR exports (5).
+
+.SH AUTHORS
+Written for the nfs-utils project.
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles
2026-05-26 14:40 [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles Benjamin Coddington
2026-05-26 14:40 ` [PATCH 1/1] nfs-fh-verify: add tool to validate kNFSD filehandle signatures Benjamin Coddington
@ 2026-05-26 15:37 ` Chuck Lever
2026-05-26 16:13 ` Benjamin Coddington
1 sibling, 1 reply; 4+ messages in thread
From: Chuck Lever @ 2026-05-26 15:37 UTC (permalink / raw)
To: Benjamin Coddington, Steve Dickson; +Cc: linux-nfs, Chuck Lever, Jeff Layton
On Tue, May 26, 2026, at 10:40 AM, Benjamin Coddington wrote:
> We've had a lot of folks configure signed filehandles, and then wonder "Is
> this thing actually working?".
Do you know if wireshark might be able to handle this verification instead?
It might be quite useful to see a "FH Verified" on every RPC in a network
capture.
> Given the various filehandle lengths and
> types, it can be pretty difficult to know if the kernel is actually
> correctly signing its filehandles. Claude and I wrote this tool that allows
> anyone to verify the correct functioning of filehandle signing given the
> original key and a list of filehandles.
--
Chuck Lever
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles
2026-05-26 15:37 ` [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles Chuck Lever
@ 2026-05-26 16:13 ` Benjamin Coddington
0 siblings, 0 replies; 4+ messages in thread
From: Benjamin Coddington @ 2026-05-26 16:13 UTC (permalink / raw)
To: Chuck Lever
Cc: Benjamin Coddington, Steve Dickson, linux-nfs, Chuck Lever,
Jeff Layton
On 26 May 2026, at 11:37, Chuck Lever wrote:
> On Tue, May 26, 2026, at 10:40 AM, Benjamin Coddington wrote:
>> We've had a lot of folks configure signed filehandles, and then wonder "Is
>> this thing actually working?".
>
> Do you know if wireshark might be able to handle this verification instead?
> It might be quite useful to see a "FH Verified" on every RPC in a network
> capture.
Great idea - I'm sure it would be able to do so. If we ever change the way
we hash the fh-key-file in nfs-utils, we'd have a wireshark drift problem.
But, its unlikely we'd change it in nfs-utils and trash everyone's existing
filehandles.
We'd have to build an interface to point it at the fh-key-file of course -
would that constant reconfiguration in wireshark make it less useful than
this utility?
I have a slight preference for this tool in the near-term because it
immediately solves my problem rather than having to wait for distros to
downstream wireshark work.
Ben
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-05-26 16:13 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-26 14:40 [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles Benjamin Coddington
2026-05-26 14:40 ` [PATCH 1/1] nfs-fh-verify: add tool to validate kNFSD filehandle signatures Benjamin Coddington
2026-05-26 15:37 ` [PATCH 0/1] nfs-utils: nfs-fh-verify signed filehandles Chuck Lever
2026-05-26 16:13 ` Benjamin Coddington
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.