If a host is a member of a large number of netgroups, it becomes easily
possible for client_compose to generate a m_hostname string that
overflows the maximum string length allowed by the kernel caches.
This patch adds a new mode for mountd where it will map IP address to IP
address in the auth.unix.ip cache. When this enabled, mountd doesn't
bother using client_compose to build the m_hostname string. It just
populates it with the dotted-quad ip address. When mountd handles a
mount request, it then has an IP address and a path. It then calls
client_check to check the host against export entries where the path has
already matched.
Since we don't bother looking up netgroups which have no relation to the
mount, this can be a big performance gain in netgroup-heavy
configurations. The downside is that every host has a corresponding
entry in the nfsd.export and nfsd.fh caches as well as the auth.unix.ip
cache.
The new behavior is automatically enabled if the length of all of the
concatenated netgroup names in the export table is longer than half
NFSCLNT_IDMAX. The rationale for this logic is that this should allow
for a host to be a member of a long list of netgroups while still
allowing for other matches.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Acked-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Neil Brown <neilb@suse.de>
static nfs_client my_client;
extern int new_cache;
static nfs_client my_client;
extern int new_cache;
void
auth_init(char *exports)
void
auth_init(char *exports)
+/*
+ * A client can match many different netgroups and it's tough to know
+ * beforehand whether it will. If the concatenated string of netgroup
+ * m_hostnames is >512 bytes, then enable the "use_ipaddr" mode. This
+ * makes mountd change how it matches a client ip address when a mount
+ * request comes in. It's more efficient at handling netgroups at the
+ * expense of larger kernel caches.
+ */
+static void
+check_useipaddr()
+{
+ nfs_client *clp;
+ int old_use_ipaddr = use_ipaddr;
+ unsigned int len = 0;
+
+ /* 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);
+
+ if (len > (NFSCLNT_IDMAX / 2))
+ use_ipaddr = 1;
+ else
+ use_ipaddr = 0;
+
+ if (use_ipaddr != old_use_ipaddr)
+ cache_flush(1);
+}
+
unsigned int
auth_reload()
{
unsigned int
auth_reload()
{
export_freeall();
memset(&my_client, 0, sizeof(my_client));
xtab_export_read();
export_freeall();
memset(&my_client, 0, sizeof(my_client));
xtab_export_read();
++counter;
return counter;
++counter;
return counter;
int i;
/* return static nfs_export with details filled in */
char *n;
int i;
/* return static nfs_export with details filled in */
char *n;
- my_client.m_addrlist[0] = caller->sin_addr;
- n = client_compose(hp);
- *error = unknown_host;
- if (!n)
- return NULL;
free(my_client.m_hostname);
free(my_client.m_hostname);
- if (*n) {
- my_client.m_hostname = n;
+ if (use_ipaddr) {
+ my_client.m_hostname =
+ strdup(inet_ntoa(caller->sin_addr));
- free(n);
- my_client.m_hostname = xstrdup("DEFAULT");
+ n = client_compose(hp);
+ *error = unknown_host;
+ if (!n)
+ my_client.m_hostname = NULL;
+ else if (*n)
+ my_client.m_hostname = n;
+ else {
+ free(n);
+ my_client.m_hostname = strdup("DEFAULT");
+ }
+ if (my_client.m_hostname == NULL)
+ return NULL;
+ my_client.m_addrlist[0] = caller->sin_addr;
my_exp.m_client = &my_client;
exp = NULL;
for (i = 0; !exp && i < MCL_MAXTYPES; i++)
for (exp = exportlist[i]; exp; exp = exp->m_next) {
my_exp.m_client = &my_client;
exp = NULL;
for (i = 0; !exp && i < MCL_MAXTYPES; i++)
for (exp = exportlist[i]; exp; exp = exp->m_next) {
- if (!client_member(my_client.m_hostname, exp->m_client->m_hostname))
- continue;
if (strcmp(path, exp->m_export.e_path))
continue;
if (strcmp(path, exp->m_export.e_path))
continue;
+ if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname))
+ continue;
+ if (use_ipaddr && !client_check(exp->m_client, hp))
+ continue;
break;
}
*error = not_exported;
break;
}
*error = not_exported;
char *lbuf = NULL;
int lbuflen = 0;
char *lbuf = NULL;
int lbuflen = 0;
void auth_unix_ip(FILE *f)
{
void auth_unix_ip(FILE *f)
{
char *cp;
char class[20];
char ipaddr[20];
char *cp;
char class[20];
char ipaddr[20];
+ struct hostent *he = NULL;
if (readline(fileno(f), &lbuf, &lbuflen) != 1)
return;
if (readline(fileno(f), &lbuf, &lbuflen) != 1)
return;
auth_reload();
/* addr is a valid, interesting address, find the domain name... */
auth_reload();
/* addr is a valid, interesting address, find the domain name... */
- he = client_resolve(addr);
- client = client_compose(he);
-
+ if (!use_ipaddr) {
+ he = client_resolve(addr);
+ client = client_compose(he);
+ }
qword_print(f, "nfsd");
qword_print(f, ipaddr);
qword_printint(f, time(0)+30*60);
qword_print(f, "nfsd");
qword_print(f, ipaddr);
qword_printint(f, time(0)+30*60);
+ if (use_ipaddr)
+ qword_print(f, ipaddr);
+ else if (client)
qword_print(f, *client?client:"DEFAULT");
qword_eol(f);
qword_print(f, *client?client:"DEFAULT");
qword_eol(f);
unsigned int fsidnum=0;
char fsid[32];
struct exportent *found = NULL;
unsigned int fsidnum=0;
char fsid[32];
struct exportent *found = NULL;
+ struct hostent *he = NULL;
+ struct in_addr addr;
char *found_path = NULL;
nfs_export *exp;
int i;
char *found_path = NULL;
nfs_export *exp;
int i;
next_exp = exp->m_next;
}
next_exp = exp->m_next;
}
- if (!client_member(dom, exp->m_client->m_hostname))
+ if (!use_ipaddr && !client_member(dom, exp->m_client->m_hostname))
continue;
if (exp->m_export.e_mountpoint &&
!is_mountpoint(exp->m_export.e_mountpoint[0]?
continue;
if (exp->m_export.e_mountpoint &&
!is_mountpoint(exp->m_export.e_mountpoint[0]?
+ if (use_ipaddr) {
+ if (he == NULL) {
+ if (!inet_aton(dom, &addr))
+ goto out;
+ he = client_resolve(addr);
+ }
+ if (!client_check(exp->m_client, he))
+ continue;
+ }
/* It's a match !! */
if (!found) {
found = &exp->m_export;
/* It's a match !! */
if (!found) {
found = &exp->m_export;
qword_eol(f);
out:
free(found_path);
qword_eol(f);
out:
free(found_path);
char *dom, *path;
nfs_export *exp, *found = NULL;
int found_type = 0;
char *dom, *path;
nfs_export *exp, *found = NULL;
int found_type = 0;
+ struct in_addr addr;
+ struct hostent *he = NULL;
if (readline(fileno(f), &lbuf, &lbuflen) != 1)
if (readline(fileno(f), &lbuf, &lbuflen) != 1)
/* now find flags for this export point in this domain */
for (i=0 ; i < MCL_MAXTYPES; i++) {
for (exp = exportlist[i]; exp; exp = exp->m_next) {
/* now find flags for this export point in this domain */
for (i=0 ; i < MCL_MAXTYPES; i++) {
for (exp = exportlist[i]; exp; exp = exp->m_next) {
- if (!client_member(dom, exp->m_client->m_hostname))
+ if (!use_ipaddr && !client_member(dom, exp->m_client->m_hostname))
continue;
if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) {
/* if path is a mountpoint below e_path, then OK */
continue;
if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) {
/* if path is a mountpoint below e_path, then OK */
continue;
} else if (strcmp(path, exp->m_export.e_path) != 0)
continue;
continue;
} else if (strcmp(path, exp->m_export.e_path) != 0)
continue;
+ if (use_ipaddr) {
+ if (he == NULL) {
+ if (!inet_aton(dom, &addr))
+ goto out;
+ he = client_resolve(addr);
+ }
+ if (!client_check(exp->m_client, he))
+ continue;
+ }
if (!found) {
found = exp;
found_type = i;
if (!found) {
found = exp;
found_type = i;
out:
if (dom) free(dom);
if (path) free(path);
out:
if (dom) free(dom);
if (path) free(path);
int reverse_resolve = 0;
int new_cache = 0;
int manage_gids;
int reverse_resolve = 0;
int new_cache = 0;
int manage_gids;
/* PRC: a high-availability callout program can be specified with -H
* When this is done, the program will receive callouts whenever clients
/* PRC: a high-availability callout program can be specified with -H
* When this is done, the program will receive callouts whenever clients