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); }