diff --git a/SOURCES/nfs-utils-2.3.3-mountd-v4-logging.patch b/SOURCES/nfs-utils-2.3.3-mountd-v4-logging.patch new file mode 100644 index 0000000..59c823d --- /dev/null +++ b/SOURCES/nfs-utils-2.3.3-mountd-v4-logging.patch @@ -0,0 +1,976 @@ +diff --git a/nfs.conf b/nfs.conf +index 05247ff9..86ed7d53 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -38,6 +38,8 @@ use-gss-proxy=1 + # reverse-lookup=n + # state-directory-path=/var/lib/nfs + # ha-callout= ++# cache-use-ipaddr=n ++# ttl=1800 + # + [nfsdcld] + # debug=0 +diff --git a/support/export/Makefile.am b/support/export/Makefile.am +index 13f7a49c..d6ee502f 100644 +--- a/support/export/Makefile.am ++++ b/support/export/Makefile.am +@@ -11,7 +11,8 @@ EXTRA_DIST = mount.x + + noinst_LIBRARIES = libexport.a + libexport_a_SOURCES = client.c export.c hostname.c \ +- xtab.c mount_clnt.c mount_xdr.c ++ xtab.c mount_clnt.c mount_xdr.c \ ++ cache.c auth.c v4root.c v4clients.c + BUILT_SOURCES = $(GENFILES) + + noinst_HEADERS = mount.h +diff --git a/utils/mountd/auth.c b/support/export/auth.c +similarity index 98% +rename from utils/mountd/auth.c +rename to support/export/auth.c +index 8299256e..73ad6f73 100644 +--- a/utils/mountd/auth.c ++++ b/support/export/auth.c +@@ -22,7 +22,7 @@ + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +-#include "mountd.h" ++#include "export.h" + #include "v4root.h" + + enum auth_error +@@ -43,11 +43,13 @@ extern int use_ipaddr; + + extern struct state_paths etab; + ++/* + void + auth_init(void) + { + auth_reload(); + } ++*/ + + /* + * A client can match many different netgroups and it's tough to know +@@ -64,6 +66,10 @@ check_useipaddr(void) + int old_use_ipaddr = use_ipaddr; + unsigned int len = 0; + ++ if (use_ipaddr > 1) ++ /* fixed - don't check */ ++ return; ++ + /* add length of m_hostname + 1 for the comma */ + for (clp = clientlist[MCL_NETGROUP]; clp; clp = clp->m_next) + len += (strlen(clp->m_hostname) + 1); +diff --git a/utils/mountd/cache.c b/support/export/cache.c +similarity index 95% +rename from utils/mountd/cache.c +rename to support/export/cache.c +index c73e29be..98d50828 100644 +--- a/utils/mountd/cache.c ++++ b/support/export/cache.c +@@ -29,21 +29,18 @@ + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +-#include "mountd.h" +-#include "fsloc.h" ++#include "export.h" + #include "pseudoflavors.h" + #include "xcommon.h" + ++#ifdef HAVE_JUNCTION_SUPPORT ++#include "../../utils/mountd/fsloc.h" ++#endif ++ + #ifdef USE_BLKID + #include "blkid/blkid.h" + #endif + +-/* +- * Invoked by RPC service loop +- */ +-void cache_set_fds(fd_set *fdset); +-int cache_process_req(fd_set *readfds); +- + enum nfsd_fsid { + FSID_DEV = 0, + FSID_NUM, +@@ -63,7 +60,6 @@ enum nfsd_fsid { + * Record is terminated with newline. + * + */ +-static int cache_export_ent(char *buf, int buflen, char *domain, struct exportent *exp, char *path); + + #define INITIAL_MANAGED_GROUPS 100 + +@@ -81,6 +77,7 @@ static void auth_unix_ip(int f) + char class[20]; + char ipaddr[INET6_ADDRSTRLEN + 1]; + char *client = NULL; ++ struct addrinfo *ai = NULL; + struct addrinfo *tmp = NULL; + char buf[RPC_CHAN_BUF_SIZE], *bp; + int blen; +@@ -106,21 +103,26 @@ static void auth_unix_ip(int f) + + auth_reload(); + +- /* addr is a valid, interesting address, find the domain name... */ +- if (!use_ipaddr) { +- struct addrinfo *ai = NULL; +- +- ai = client_resolve(tmp->ai_addr); +- if (ai) { +- client = client_compose(ai); +- freeaddrinfo(ai); +- } ++ /* addr is a valid address, find the domain name... */ ++ ai = client_resolve(tmp->ai_addr); ++ if (ai) { ++ client = client_compose(ai); ++ freeaddrinfo(ai); + } ++ if (!client) ++ xlog(D_AUTH, "failed authentication for IP %s", ipaddr); ++ else if (!use_ipaddr) ++ xlog(D_AUTH, "successful authentication for IP %s as %s", ++ ipaddr, *client ? client : "DEFAULT"); ++ else ++ xlog(D_AUTH, "successful authentication for IP %s", ++ ipaddr); ++ + bp = buf; blen = sizeof(buf); + qword_add(&bp, &blen, "nfsd"); + qword_add(&bp, &blen, ipaddr); +- qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL); +- if (use_ipaddr) { ++ qword_adduint(&bp, &blen, time(0) + default_ttl); ++ if (use_ipaddr && client) { + memmove(ipaddr + 1, ipaddr, strlen(ipaddr) + 1); + ipaddr[0] = '$'; + qword_add(&bp, &blen, ipaddr); +@@ -192,7 +194,7 @@ static void auth_unix_gid(int f) + + bp = buf; blen = sizeof(buf); + qword_adduint(&bp, &blen, uid); +- qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL); ++ qword_adduint(&bp, &blen, time(0) + default_ttl); + if (rv >= 0) { + qword_adduint(&bp, &blen, ngroups); + for (i=0; im_export.e_mountpoint && +- !is_mountpoint(exp->m_export.e_mountpoint[0]? +- exp->m_export.e_mountpoint: +- exp->m_export.e_path)) +- dev_missing ++; + + if (!match_fsid(&parsed, exp, path)) + continue; +@@ -794,7 +790,7 @@ static void nfsd_fh(int f) + !is_mountpoint(found->e_mountpoint[0]? + found->e_mountpoint: + found->e_path)) { +- /* Cannot export this yet ++ /* Cannot export this yet + * should log a warning, but need to rate limit + xlog(L_WARNING, "%s not exported as %d not a mountpoint", + found->e_path, found->e_mountpoint); +@@ -802,16 +798,6 @@ static void nfsd_fh(int f) + /* FIXME we need to make sure we re-visit this later */ + goto out; + } +- if (!found && dev_missing) { +- /* The missing dev could be what we want, so just be +- * quite rather than returning stale yet +- */ +- goto out; +- } +- +- if (found) +- if (cache_export_ent(buf, sizeof(buf), dom, found, found_path) < 0) +- found = 0; + + bp = buf; blen = sizeof(buf); + qword_add(&bp, &blen, dom); +@@ -831,6 +817,8 @@ static void nfsd_fh(int f) + qword_addeol(&bp, &blen); + if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) + xlog(L_ERROR, "nfsd_fh: error writing reply"); ++ if (!found) ++ xlog(D_AUTH, "denied access to %s", *dom == '$' ? dom+1 : dom); + out: + if (found_path) + free(found_path); +@@ -839,6 +827,7 @@ out: + xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL); + } + ++#ifdef HAVE_JUNCTION_SUPPORT + static void write_fsloc(char **bp, int *blen, struct exportent *ep) + { + struct servers *servers; +@@ -861,7 +850,7 @@ static void write_fsloc(char **bp, int *blen, struct exportent *ep) + qword_addint(bp, blen, servers->h_referral); + release_replicas(servers); + } +- ++#endif + static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask) + { + struct sec_entry *p; +@@ -890,7 +879,7 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain, + time_t now = time(0); + + if (ttl <= 1) +- ttl = DEFAULT_TTL; ++ ttl = default_ttl; + + qword_add(&bp, &blen, domain); + qword_add(&bp, &blen, path); +@@ -903,7 +892,10 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain, + qword_addint(&bp, &blen, exp->e_anonuid); + qword_addint(&bp, &blen, exp->e_anongid); + qword_addint(&bp, &blen, exp->e_fsid); ++ ++#ifdef HAVE_JUNCTION_SUPPORT + write_fsloc(&bp, &blen, exp); ++#endif + write_secinfo(&bp, &blen, exp, flag_mask); + if (exp->e_uuid == NULL || different_fs) { + char u[16]; +@@ -917,8 +909,13 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain, + qword_add(&bp, &blen, "uuid"); + qword_addhex(&bp, &blen, u, 16); + } +- } else ++ xlog(D_AUTH, "granted access to %s for %s", ++ path, *domain == '$' ? domain+1 : domain); ++ } else { + qword_adduint(&bp, &blen, now + ttl); ++ xlog(D_AUTH, "denied access to %s for %s", ++ path, *domain == '$' ? domain+1 : domain); ++ } + qword_addeol(&bp, &blen); + if (blen <= 0) return -1; + if (write(f, buf, bp - buf) != bp - buf) return -1; +@@ -1421,6 +1418,40 @@ int cache_process_req(fd_set *readfds) + return cnt; + } + ++/** ++ * cache_process_loop - process incoming upcalls ++ */ ++void cache_process_loop(void) ++{ ++ fd_set readfds; ++ int selret; ++ ++ FD_ZERO(&readfds); ++ ++ for (;;) { ++ ++ cache_set_fds(&readfds); ++ v4clients_set_fds(&readfds); ++ ++ selret = select(FD_SETSIZE, &readfds, ++ (void *) 0, (void *) 0, (struct timeval *) 0); ++ ++ ++ switch (selret) { ++ case -1: ++ if (errno == EINTR || errno == ECONNREFUSED ++ || errno == ENETUNREACH || errno == EHOSTUNREACH) ++ continue; ++ xlog(L_ERROR, "my_svc_run() - select: %m"); ++ return; ++ ++ default: ++ cache_process_req(&readfds); ++ v4clients_process(&readfds); ++ } ++ } ++} ++ + + /* + * Give IP->domain and domain+path->options to kernel +diff --git a/support/export/export.h b/support/export/export.h +new file mode 100644 +index 00000000..8d5a0d30 +--- /dev/null ++++ b/support/export/export.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (C) 2021 Red Hat ++ * ++ * support/export/export.h ++ * ++ * Declarations for export support ++ */ ++ ++#ifndef EXPORT_H ++#define EXPORT_H ++ ++#include "nfslib.h" ++#include "exportfs.h" ++ ++unsigned int auth_reload(void); ++nfs_export * auth_authenticate(const char *what, ++ const struct sockaddr *caller, ++ const char *path); ++ ++void cache_open(void); ++void cache_set_fds(fd_set *fdset); ++int cache_process_req(fd_set *readfds); ++void cache_process_loop(void); ++ ++void v4clients_init(void); ++void v4clients_set_fds(fd_set *fdset); ++int v4clients_process(fd_set *fdset); ++ ++struct nfs_fh_len * ++ cache_get_filehandle(nfs_export *exp, int len, char *p); ++int cache_export(nfs_export *exp, char *path); ++ ++bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai); ++bool namelist_client_matches(nfs_export *exp, char *dom); ++bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai); ++ ++static inline bool is_ipaddr_client(char *dom) ++{ ++ return dom[0] == '$'; ++} ++#endif /* EXPORT__H */ +diff --git a/support/export/v4clients.c b/support/export/v4clients.c +new file mode 100644 +index 00000000..dd985463 +--- /dev/null ++++ b/support/export/v4clients.c +@@ -0,0 +1,227 @@ ++/* ++ * support/export/v4clients.c ++ * ++ * Montior clients appearing in, and disappearing from, /proc/fs/nfsd/clients ++ * and log relevant information. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "export.h" ++ ++/* search.h declares 'struct entry' and nfs_prot.h ++ * does too. Easiest fix is to trick search.h into ++ * calling its struct "struct Entry". ++ */ ++#define entry Entry ++#include ++#undef entry ++ ++static int clients_fd = -1; ++ ++void v4clients_init(void) ++{ ++ if (clients_fd >= 0) ++ return; ++ clients_fd = inotify_init1(IN_NONBLOCK); ++ if (clients_fd < 0) { ++ xlog_err("Unable to initialise v4clients watcher: %s\n", ++ strerror(errno)); ++ return; ++ } ++ if (inotify_add_watch(clients_fd, "/proc/fs/nfsd/clients", ++ IN_CREATE | IN_DELETE) < 0) { ++ xlog_err("Unable to watch /proc/fs/nfsd/clients: %s\n", ++ strerror(errno)); ++ close(clients_fd); ++ clients_fd = -1; ++ return; ++ } ++} ++ ++void v4clients_set_fds(fd_set *fdset) ++{ ++ if (clients_fd >= 0) ++ FD_SET(clients_fd, fdset); ++} ++ ++static void *tree_root; ++static int have_unconfirmed; ++ ++struct ent { ++ unsigned long num; ++ char *clientid; ++ char *addr; ++ int vers; ++ int unconfirmed; ++ int wid; ++}; ++ ++static int ent_cmp(const void *av, const void *bv) ++{ ++ const struct ent *a = av; ++ const struct ent *b = bv; ++ ++ if (a->num < b->num) ++ return -1; ++ if (a->num > b->num) ++ return 1; ++ return 0; ++} ++ ++static void free_ent(struct ent *ent) ++{ ++ free(ent->clientid); ++ free(ent->addr); ++ free(ent); ++} ++ ++static char *dup_line(char *line) ++{ ++ char *ret; ++ char *e = strchr(line, '\n'); ++ if (!e) ++ e = line + strlen(line); ++ ret = malloc(e - line + 1); ++ if (ret) { ++ memcpy(ret, line, e - line); ++ ret[e-line] = 0; ++ } ++ return ret; ++} ++ ++static void read_info(struct ent *key) ++{ ++ char buf[2048]; ++ char *path; ++ int was_unconfirmed = key->unconfirmed; ++ FILE *f; ++ ++ if (asprintf(&path, "/proc/fs/nfsd/clients/%lu/info", key->num) < 0) ++ return; ++ ++ f = fopen(path, "r"); ++ if (!f) { ++ free(path); ++ return; ++ } ++ if (key->wid < 0) ++ key->wid = inotify_add_watch(clients_fd, path, IN_MODIFY); ++ ++ while (fgets(buf, sizeof(buf), f)) { ++ if (strncmp(buf, "clientid: ", 10) == 0) { ++ free(key->clientid); ++ key->clientid = dup_line(buf+10); ++ } ++ if (strncmp(buf, "address: ", 9) == 0) { ++ free(key->addr); ++ key->addr = dup_line(buf+9); ++ } ++ if (strncmp(buf, "minor version: ", 15) == 0) ++ key->vers = atoi(buf+15); ++ if (strncmp(buf, "status: ", 8) == 0 && ++ strstr(buf, " unconfirmed") != NULL) { ++ key->unconfirmed = 1; ++ have_unconfirmed = 1; ++ } ++ if (strncmp(buf, "status: ", 8) == 0 && ++ strstr(buf, " confirmed") != NULL) ++ key->unconfirmed = 0; ++ } ++ fclose(f); ++ free(path); ++ ++ if (was_unconfirmed && !key->unconfirmed) ++ xlog(L_NOTICE, "v4.%d client attached: %s from %s", ++ key->vers, key->clientid ?: "-none-", ++ key->addr ?: "-none-"); ++ if (!key->unconfirmed && key->wid >= 0) { ++ inotify_rm_watch(clients_fd, key->wid); ++ key->wid = -1; ++ } ++} ++ ++static void add_id(int id) ++{ ++ struct ent **ent; ++ struct ent *key; ++ ++ key = calloc(1, sizeof(*key)); ++ if (!key) { ++ return; ++ } ++ key->num = id; ++ key->wid = -1; ++ ++ ent = tsearch(key, &tree_root, ent_cmp); ++ ++ if (!ent || *ent != key) ++ /* Already existed, or insertion failed */ ++ free_ent(key); ++ else ++ read_info(key); ++} ++ ++static void del_id(unsigned long id) ++{ ++ struct ent key = {.num = id}; ++ struct ent **e, *ent; ++ ++ e = tfind(&key, &tree_root, ent_cmp); ++ if (!e || !*e) ++ return; ++ ent = *e; ++ tdelete(ent, &tree_root, ent_cmp); ++ if (!ent->unconfirmed) ++ xlog(L_NOTICE, "v4.%d client detached: %s from %s", ++ ent->vers, ent->clientid, ent->addr); ++ if (ent->wid >= 0) ++ inotify_rm_watch(clients_fd, ent->wid); ++ free_ent(ent); ++} ++ ++static void check_id(unsigned long id) ++{ ++ struct ent key = {.num = id}; ++ struct ent **e, *ent; ++ ++ e = tfind(&key, &tree_root, ent_cmp); ++ if (!e || !*e) ++ return; ++ ent = *e; ++ if (ent->unconfirmed) ++ read_info(ent); ++} ++ ++int v4clients_process(fd_set *fdset) ++{ ++ char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); ++ const struct inotify_event *ev; ++ ssize_t len; ++ char *ptr; ++ ++ if (clients_fd < 0 || ++ !FD_ISSET(clients_fd, fdset)) ++ return 0; ++ ++ while ((len = read(clients_fd, buf, sizeof(buf))) > 0) { ++ for (ptr = buf; ptr < buf + len; ++ ptr += sizeof(struct inotify_event) + ev->len) { ++ int id; ++ ev = (const struct inotify_event *)ptr; ++ ++ id = atoi(ev->name); ++ if (id <= 0) ++ continue; ++ if (ev->mask & IN_CREATE) ++ add_id(id); ++ if (ev->mask & IN_DELETE) ++ del_id(id); ++ if (ev->mask & IN_MODIFY) ++ check_id(id); ++ } ++ } ++ return 1; ++} +diff --git a/utils/mountd/v4root.c b/support/export/v4root.c +similarity index 99% +rename from utils/mountd/v4root.c +rename to support/export/v4root.c +index 8ec33fb0..4d33117f 100644 +--- a/utils/mountd/v4root.c ++++ b/support/export/v4root.c +@@ -47,7 +47,7 @@ static nfs_export pseudo_root = { + .e_nsqgids = 0, + .e_fsid = 0, + .e_mountpoint = NULL, +- .e_ttl = DEFAULT_TTL, ++ .e_ttl = 0, + }, + .m_exported = 0, + .m_xtabent = 1, +@@ -86,6 +86,7 @@ v4root_create(char *path, nfs_export *export) + struct exportent *curexp = &export->m_export; + + dupexportent(&eep, &pseudo_root.m_export); ++ eep.e_ttl = default_ttl; + eep.e_hostname = curexp->e_hostname; + strncpy(eep.e_path, path, sizeof(eep.e_path)-1); + if (strcmp(path, "/") != 0) +diff --git a/support/include/exportfs.h b/support/include/exportfs.h +index 4e0d9d13..bfae1957 100644 +--- a/support/include/exportfs.h ++++ b/support/include/exportfs.h +@@ -105,7 +105,8 @@ typedef struct mexport { + } nfs_export; + + #define HASH_TABLE_SIZE 1021 +-#define DEFAULT_TTL (30 * 60) ++ ++extern int default_ttl; + + typedef struct _exp_hash_entry { + nfs_export * p_first; +diff --git a/support/nfs/exports.c b/support/nfs/exports.c +index a7582cae..4dd2e5d3 100644 +--- a/support/nfs/exports.c ++++ b/support/nfs/exports.c +@@ -47,6 +47,8 @@ struct flav_info flav_map[] = { + + const int flav_map_size = sizeof(flav_map)/sizeof(flav_map[0]); + ++int default_ttl = 30 * 60; ++ + static char *efname = NULL; + static XFILE *efp = NULL; + static int first; +@@ -100,7 +102,7 @@ static void init_exportent (struct exportent *ee, int fromkernel) + ee->e_nsquids = 0; + ee->e_nsqgids = 0; + ee->e_uuid = NULL; +- ee->e_ttl = DEFAULT_TTL; ++ ee->e_ttl = default_ttl; + } + + struct exportent * +diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man +index 498d93a9..aa4630bb 100644 +--- a/systemd/nfs.conf.man ++++ b/systemd/nfs.conf.man +@@ -157,6 +157,8 @@ Recognized values: + .BR port , + .BR threads , + .BR reverse-lookup , ++.BR cache-use-upaddr , ++.BR ttl , + .BR state-directory-path , + .BR ha-callout . + +@@ -166,6 +168,14 @@ section, are used to configure mountd. See + .BR rpc.mountd (8) + for details. + ++Note that setting ++.B "\[dq]debug = auth\[dq]" ++for ++.B mountd ++is equivalent to providing the ++.B \-\-log\-auth ++option. ++ + The + .B state-directory-path + value in the +diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am +index 73eeb3f3..c41f06de 100644 +--- a/utils/mountd/Makefile.am ++++ b/utils/mountd/Makefile.am +@@ -13,8 +13,8 @@ KPREFIX = @kprefix@ + sbin_PROGRAMS = mountd + + noinst_HEADERS = fsloc.h +-mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \ +- svc_run.c fsloc.c v4root.c mountd.h ++mountd_SOURCES = mountd.c mount_dispatch.c rmtab.c \ ++ svc_run.c fsloc.c mountd.h + mountd_LDADD = ../../support/export/libexport.a \ + ../../support/nfs/libnfs.la \ + ../../support/misc/libmisc.a \ +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c +index 0b891121..2b342377 100644 +--- a/utils/mountd/mountd.c ++++ b/utils/mountd/mountd.c +@@ -30,6 +30,7 @@ + #include "rpcmisc.h" + #include "pseudoflavors.h" + #include "nfslib.h" ++#include "export.h" + + extern void my_svc_run(void); + +@@ -73,8 +74,12 @@ static struct option longopts[] = + { "reverse-lookup", 0, 0, 'r' }, + { "manage-gids", 0, 0, 'g' }, + { "no-udp", 0, 0, 'u' }, ++ { "log-auth", 0, 0, 'l'}, ++ { "cache-use-ipaddr", 0, 0, 'i'}, ++ { "ttl", 1, 0, 'T'}, + { NULL, 0, 0, 0 } + }; ++static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:"; + + #define NFSVERSBIT(vers) (0x1 << (vers - 1)) + #define NFSVERSBIT_ALL (NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4)) +@@ -669,6 +674,7 @@ main(int argc, char **argv) + int port = 0; + int descriptors = 0; + int c; ++ int ttl; + int vers; + struct sigaction sa; + struct rlimit rlim; +@@ -687,6 +693,8 @@ main(int argc, char **argv) + num_threads = conf_get_num("mountd", "threads", num_threads); + reverse_resolve = conf_get_bool("mountd", "reverse-lookup", reverse_resolve); + ha_callout_prog = conf_get_str("mountd", "ha-callout"); ++ if (conf_get_bool("mountd", "cache-use-ipaddr", 0)) ++ use_ipaddr = 2; + + s = conf_get_str("mountd", "state-directory-path"); + if (s && !state_setup_basedir(argv[0], s)) +@@ -710,10 +718,13 @@ main(int argc, char **argv) + NFSCTL_VERUNSET(nfs_version, vers); + } + ++ ttl = conf_get_num("mountd", "ttl", default_ttl); ++ if (ttl > 0) ++ default_ttl = ttl; + + /* Parse the command line options and arguments. */ + opterr = 0; +- while ((c = getopt_long(argc, argv, "o:nFd:p:P:hH:N:V:vurs:t:g", longopts, NULL)) != EOF) ++ while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF) + switch (c) { + case 'g': + manage_gids = 1; +@@ -784,6 +795,21 @@ main(int argc, char **argv) + case 'u': + NFSCTL_UDPUNSET(_rpcprotobits); + break; ++ case 'l': ++ xlog_sconfig("auth", 1); ++ break; ++ case 'i': ++ use_ipaddr = 2; ++ break; ++ case 'T': ++ ttl = atoi(optarg); ++ if (ttl <= 0) { ++ fprintf(stderr, "%s: bad ttl number of seconds: %s\n", ++ argv[0], optarg); ++ usage(argv[0], 1); ++ } ++ default_ttl = ttl; ++ break; + case 0: + break; + case '?': +@@ -888,6 +914,8 @@ main(int argc, char **argv) + if (num_threads > 1) + fork_workers(); + ++ v4clients_init(); ++ + xlog(L_NOTICE, "Version " VERSION " starting"); + my_svc_run(); + +@@ -903,6 +931,7 @@ usage(const char *prog, int n) + { + fprintf(stderr, + "Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n" ++" [-l|--log-auth] [-i|--cache-use-ipaddr] [-T|--ttl ttl]\n" + " [-o num|--descriptors num]\n" + " [-p|--port port] [-V version|--nfs-version version]\n" + " [-N version|--no-nfs-version version] [-n|--no-tcp]\n" +diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h +index f058f01d..d3077531 100644 +--- a/utils/mountd/mountd.h ++++ b/utils/mountd/mountd.h +@@ -60,9 +60,4 @@ bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai); + bool namelist_client_matches(nfs_export *exp, char *dom); + bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai); + +-static inline bool is_ipaddr_client(char *dom) +-{ +- return dom[0] == '$'; +-} +- + #endif /* MOUNTD_H */ +diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man +index 8a7943f8..2a91e193 100644 +--- a/utils/mountd/mountd.man ++++ b/utils/mountd/mountd.man +@@ -13,24 +13,24 @@ The + .B rpc.mountd + daemon implements the server side of the NFS MOUNT protocol, + an NFS side protocol used by NFS version 2 [RFC1094] and NFS version 3 [RFC1813]. ++It also responds to requests from the Linux kernel to authenticate ++clients and provides details of access permissions. + .PP +-An NFS server maintains a table of local physical file systems +-that are accessible to NFS clients. +-Each file system in this table is referred to as an +-.IR "exported file system" , +-or +-.IR export , +-for short. +-.PP +-Each file system in the export table has an access control list. +-.B rpc.mountd +-uses these access control lists to determine +-whether an NFS client is permitted to access a given file system. +-For details on how to manage your NFS server's export table, see the +-.BR exports (5) +-and +-.BR exportfs (8) +-man pages. ++The NFS server ++.RI ( nfsd ) ++maintains a cache of authentication and authorization information which ++is used to identify the source of each request, and then what access ++permissions that source has to any local filesystem. When required ++information is not found in the cache, the server sends a request to ++.B mountd ++to fill in the missing information. Mountd uses a table of information ++stored in ++.B /var/lib/nfs/etab ++and maintained by ++.BR exportfs (8), ++possibly based on the contents of ++.BR exports (5), ++to respond to each request. + .SS Mounting exported NFS File Systems + The NFS MOUNT protocol has several procedures. + The most important of these are +@@ -78,11 +78,69 @@ A client may continue accessing an export even after invoking UMNT. + If the client reboots without sending a UMNT request, stale entries + remain for that client in + .IR /var/lib/nfs/rmtab . ++.SS Mounting File Systems with NFSv4 ++Version 4 (and later) of NFS does not use a separate NFS MOUNT ++protocol. Instead mounting is performed using regular NFS requests ++handled by the NFS server in the Linux kernel ++.RI ( nfsd ). ++Consequently ++.I /var/lib/nfs/rmtab ++is not updated to reflect any NFSv4 activity. + .SH OPTIONS + .TP + .B \-d kind " or " \-\-debug kind + Turn on debugging. Valid kinds are: all, auth, call, general and parse. + .TP ++.BR \-l " or " \-\-log\-auth ++Enable logging of responses to authentication and access requests from ++nfsd. Each response is then cached by the kernel for 30 minutes (or as set by ++.B \-\-ttl ++below), and will be refreshed after 15 minutes (half the ttl time) if ++the relevant client remains active. ++Note that ++.B -l ++is equivalent to ++.B "-d auth" ++and so can be enabled in ++.B /etc/nfs.conf ++with ++.B "\[dq]debug = auth\[dq]" ++in the ++.B "[mountd]" ++section. ++.IP ++.B rpc.mountd ++will always log authentication responses to MOUNT requests when NFSv3 is ++used, but to get similar logs for NFSv4, this option is required. ++.TP ++.BR \-i " or " \-\-cache\-use\-ipaddr ++Normally each client IP address is matched against each host identifier ++(name, wildcard, netgroup etc) found in ++.B /etc/exports ++and a combined identity is formed from all matching identifiers. ++Often many clients will map to the same combined identity so performing ++this mapping reduces the number of distinct access details that the ++kernel needs to store. ++Specifying the ++.B \-i ++option suppresses this mapping so that access to each filesystem is ++requested and cached separately for each client IP address. Doing this ++can increase the burden of updating the cache slightly, but can make the ++log messages produced by the ++.B -l ++option easier to read. ++.TP ++.B \-T " or " \-\-ttl ++Provide a time-to-live (TTL) for cached information given to the kernel. ++The kernel will normally request an update if the information is needed ++after half of this time has expired. Increasing the provided number, ++which is in seconds, reduces the rate of cache update requests, and this ++is particularly noticeable when these requests are logged with ++.BR \-l . ++However increasing also means that changes to hostname to address ++mappings can take longer to be noticed. ++The default TTL is 1800 (30 minutes). ++.TP + .B \-F " or " \-\-foreground + Run in foreground (do not daemonize) + .TP +@@ -213,9 +271,11 @@ Values recognized in the + .B [mountd] + section include + .BR manage-gids , ++.BR cache\-use\-ipaddr , + .BR descriptors , + .BR port , + .BR threads , ++.BR ttl , + .BR reverse-lookup ", and" + .BR state-directory-path , + .B ha-callout +@@ -265,5 +325,9 @@ table of clients accessing server's exports + RFC 1094 - "NFS: Network File System Protocol Specification" + .br + RFC 1813 - "NFS Version 3 Protocol Specification" ++.br ++RFC 7530 - "Network File System (NFS) Version 4 Protocol" ++.br ++RFC 8881 - "Network File System (NFS) Version 4 Minor Version 1 Protocol" + .SH AUTHOR + Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others. +diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c +index 41b96d7f..167b9757 100644 +--- a/utils/mountd/svc_run.c ++++ b/utils/mountd/svc_run.c +@@ -56,10 +56,9 @@ + #ifdef HAVE_LIBTIRPC + #include + #endif ++#include "export.h" + + void my_svc_run(void); +-void cache_set_fds(fd_set *fdset); +-int cache_process_req(fd_set *readfds); + + #if defined(__GLIBC__) && LONG_MAX != INT_MAX + /* bug in glibc 2.3.6 and earlier, we need +@@ -101,6 +100,7 @@ my_svc_run(void) + + readfds = svc_fdset; + cache_set_fds(&readfds); ++ v4clients_set_fds(&readfds); + + selret = select(FD_SETSIZE, &readfds, + (void *) 0, (void *) 0, (struct timeval *) 0); +@@ -116,6 +116,7 @@ my_svc_run(void) + + default: + selret -= cache_process_req(&readfds); ++ selret -= v4clients_process(&readfds); + if (selret) + svc_getreqset(&readfds); + } diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec index 345fa9f..72e24c7 100644 --- a/SPECS/nfs-utils.spec +++ b/SPECS/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://linux-nfs.org/ Version: 2.3.3 -Release: 46%{?dist} +Release: 47%{?dist} Epoch: 1 # group all 32bit related archs @@ -85,6 +85,9 @@ Patch043: nfs-utils-2.3.3-gssd-timeout-thread.patch Patch044: nfs-utils-2.3.3-gssd-debug-cleanup.patch Patch045: nfs-utils-2.3.3-gssd-mutex-refcnt.patch +# +# RHEL 8.6 +Patch046: nfs-utils-2.3.3-mountd-v4-logging.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -360,6 +363,9 @@ fi %{_libdir}/libnfsidmap.so %changelog +* Tue Nov 2 2021 Steve Dickson 2.3.3-47 +- Enable logging for NFSv4 mount requests (bz 2004151) + * Wed Jul 28 2021 Steve Dickson 2.3.3-46 - mount.nfs: Fix the sloppy option processing (bz 1967883)