Add support for the experimental nfs client to mount_nfs. The

experimental client is used when the fstype is "newnfs" or the "nfsv4"
option is specified. It includes the addition of the option:
  gssname - to specify a client side initiator host based principal name
which is specific to NFSv4.
It also includes a change to mount.c, so that it knows about
mount_newnfs, but not mount_nfs4.

Reviewed by:	dfr
Approved by:	kib (mentor)
This commit is contained in:
rmacklem 2009-05-27 19:56:51 +00:00
parent b36d17c973
commit 08f63a0d18
4 changed files with 173 additions and 8 deletions

View File

@ -140,7 +140,7 @@ use_mountprog(const char *vfstype)
*/ */
unsigned int i; unsigned int i;
const char *fs[] = { const char *fs[] = {
"cd9660", "mfs", "msdosfs", "nfs", "nfs4", "ntfs", "cd9660", "mfs", "msdosfs", "newnfs", "nfs", "ntfs",
"nwfs", "nullfs", "portalfs", "smbfs", "udf", "unionfs", "nwfs", "nullfs", "portalfs", "smbfs", "udf", "unionfs",
NULL NULL
}; };

View File

@ -5,13 +5,15 @@
PROG= mount_nfs PROG= mount_nfs
SRCS= mount_nfs.c getmntopts.c mounttab.c SRCS= mount_nfs.c getmntopts.c mounttab.c
MAN= mount_nfs.8 MAN= mount_nfs.8
MLINKS= mount_nfs.8 MLINKS= mount_nfs.8 mount_newnfs.8
MOUNT= ${.CURDIR}/../mount MOUNT= ${.CURDIR}/../mount
UMNTALL= ${.CURDIR}/../../usr.sbin/rpc.umntall UMNTALL= ${.CURDIR}/../../usr.sbin/rpc.umntall
CFLAGS+= -DNFS -I${MOUNT} -I${UMNTALL} CFLAGS+= -DNFS -I${MOUNT} -I${UMNTALL}
WARNS?= 3 WARNS?= 3
LINKS= ${BINDIR}/mount_nfs ${BINDIR}/mount_newnfs
.PATH: ${MOUNT} ${UMNTALL} .PATH: ${MOUNT} ${UMNTALL}
.include <bsd.prog.mk> .include <bsd.prog.mk>

View File

@ -132,6 +132,47 @@ short.
.It Cm fg .It Cm fg
Same as not specifying Same as not specifying
.Cm bg . .Cm bg .
.It Cm gssname Ns = Ns Aq Ar name
For the RPCSEC_GSS security flavors, such as krb5, krb5i and krb5p when being
used for an NFSv4 mount, this option specifies the host based principal
name to be used for the state related operations SetClientID,
SetClientIDConfirm, ReleaseLockOwner and Renew.
It is also used for other operations, such as Getattr for
.Xr statfs 2
information and during open/lock state recovery.
An entry for this principal must exist
in the client machine's default keytab file.
If possible, the keytab entry should be created using DES_CBC_CRC
encryption. If another encryption algorithm is used, the sysctl variable
.Va vfs.newnfs.keytab_enctype
must be set to the numeric value representing that encryption algorithm.
(The numeric values can be found in /usr/include/krb5_asn1.h. Look
for constants named ETYPE_xxx.)
If this option is given
as a name without an ``@<client-fqdn>'', such as ``root'' or ``nfs'',
``@<client-fqdn>'' will be appended to it.
.sp
If this option is not specified
for NFSv4 mounts using krb5[ip], the above operations will be done using the
user principal for the user that performed the mount. This
only works for mounts done by a user other than ``root'' and the user must
have a valid TGT in their credentials cache at the time the mount is done.
(Setting the
.Va vfs.usermount
to non-zero will allow users to do mounts.)
Because the user's TGT is used to acquire credentials for these operations,
it is important that that user's TGT does not expire before
.Xr umount 8
is done.
.It Cm allgssname
This option can be used along with
.Cm gssname
to indicate that all accesses to the mount point are to be done using
the host based principal specified by the
.Cm gssname
option.
This might be useful for nfsv4 mounts using sec=krb5[ip] that are being accessed
by batch utilities over long periods of time.
.It Cm hard .It Cm hard
Same as not specifying Same as not specifying
.Cm soft . .Cm soft .
@ -157,6 +198,12 @@ then version 2).
Note that NFS version 2 has a file size limit of 2 gigabytes. Note that NFS version 2 has a file size limit of 2 gigabytes.
.It Cm nfsv3 .It Cm nfsv3
Use the NFS Version 3 protocol. Use the NFS Version 3 protocol.
.It Cm nfsv4
Use the NFS Version 4 protocol.
This option will force the mount to use the experimental nfs subsystem and
TCP transport.
To use the experimental nfs subsystem for nfsv2 and nfsv3 mounts, you
must specify the ``newnfs'' file system type instead of ``nfs''.
.It Cm noconn .It Cm noconn
For UDP mount points, do not do a For UDP mount points, do not do a
.Xr connect 2 . .Xr connect 2 .
@ -192,6 +239,11 @@ servers on the client.
Note that this option will only be honored when performing the Note that this option will only be honored when performing the
initial mount, it will be silently ignored if used while updating initial mount, it will be silently ignored if used while updating
the mount options. the mount options.
.It Cm principal
For the RPCSEC_GSS security flavors, such as krb5, krb5i and krb5p,
this option sets the name of the host based principal name expected
by the server. This option overrides the default, which will be
``nfs@<server-fqdn>'' and should normally be sufficient.
.It Cm noresvport .It Cm noresvport
Do Do
.Em not .Em not
@ -200,8 +252,10 @@ use a reserved socket port number (see below).
Use specified port number for NFS requests. Use specified port number for NFS requests.
The default is to query the portmapper for the NFS port. The default is to query the portmapper for the NFS port.
.It Cm rdirplus .It Cm rdirplus
Used with NQNFS and NFSV3 to specify that the \fBReaddirPlus\fR RPC should Used with NFSV3 to specify that the \fBReaddirPlus\fR RPC should
be used. be used.
For NFSV4, setting this option has a similar effect, in that it will make
the Readdir Operation get more attributes.
This option reduces RPC traffic for cases such as This option reduces RPC traffic for cases such as
.Dq "ls -l" , .Dq "ls -l" ,
but tends to flood the attribute and name caches with prefetched entries. but tends to flood the attribute and name caches with prefetched entries.
@ -248,6 +302,18 @@ with the
option to see what the option to see what the
.Dq "fragments dropped due to timeout" .Dq "fragments dropped due to timeout"
value is.) value is.)
.It Cm sec Ns = Ns Aq Ar flavor
This option specifies what security flavor should be used for the mount.
Currently, they are:
.Bd -literal
krb5 - Use KerberosV authentication
krb5i - Use KerberosV authentication and
apply integrity checksums to RPCs
krb5p - Use KerberosV authentication and
encrypt the RPC data
sys - The default AUTH_SYS, which uses a
uid + gid list authenticator
.Ed
.It Cm soft .It Cm soft
A soft mount, which implies that file system calls will fail A soft mount, which implies that file system calls will fail
after after
@ -368,8 +434,19 @@ Same as
.Sh SEE ALSO .Sh SEE ALSO
.Xr nmount 2 , .Xr nmount 2 ,
.Xr unmount 2 , .Xr unmount 2 ,
.Xr nfsv4 4 ,
.Xr fstab 5 , .Xr fstab 5 ,
.Xr gssd 8 ,
.Xr mount 8 , .Xr mount 8 ,
.Xr nfsd 8 , .Xr nfsd 8 ,
.Xr nfsiod 8 , .Xr nfsiod 8 ,
.Xr showmount 8 .Xr showmount 8
.Sh BUGS
Since nfsv4 performs open/lock operations that have their ordering strictly
enforced by the server, the options
.Cm intr
and
.Cm soft
cannot be safely used.
.Cm hard
nfsv4 mounts are strongly recommended.

View File

@ -45,6 +45,8 @@ static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95";
__FBSDID("$FreeBSD$"); __FBSDID("$FreeBSD$");
#include <sys/param.h> #include <sys/param.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -111,11 +113,13 @@ int addrlen = 0;
u_char *fh = NULL; u_char *fh = NULL;
int fhsize = 0; int fhsize = 0;
int secflavor = -1; int secflavor = -1;
int got_principal = 0;
enum mountmode { enum mountmode {
ANY, ANY,
V2, V2,
V3, V3,
V4
} mountmode = ANY; } mountmode = ANY;
/* Return codes for nfs_tryproto. */ /* Return codes for nfs_tryproto. */
@ -150,11 +154,13 @@ main(int argc, char *argv[])
int osversion; int osversion;
char *name, *p, *spec, *fstype; char *name, *p, *spec, *fstype;
char mntpath[MAXPATHLEN], errmsg[255]; char mntpath[MAXPATHLEN], errmsg[255];
char hostname[MAXHOSTNAMELEN + 1], *gssname, gssn[MAXHOSTNAMELEN + 50];
mntflags = 0; mntflags = 0;
iov = NULL; iov = NULL;
iovlen = 0; iovlen = 0;
memset(errmsg, 0, sizeof(errmsg)); memset(errmsg, 0, sizeof(errmsg));
gssname = NULL;
fstype = strrchr(argv[0], '_'); fstype = strrchr(argv[0], '_');
if (fstype == NULL) if (fstype == NULL)
@ -242,6 +248,9 @@ main(int argc, char *argv[])
} else if (strcmp(opt, "fg") == 0) { } else if (strcmp(opt, "fg") == 0) {
/* same as not specifying -o bg */ /* same as not specifying -o bg */
pass_flag_to_nmount=0; pass_flag_to_nmount=0;
} else if (strcmp(opt, "gssname") == 0) {
pass_flag_to_nmount = 0;
gssname = val;
} else if (strcmp(opt, "mntudp") == 0) { } else if (strcmp(opt, "mntudp") == 0) {
mnttcp_ok = 0; mnttcp_ok = 0;
nfsproto = IPPROTO_UDP; nfsproto = IPPROTO_UDP;
@ -262,12 +271,21 @@ main(int argc, char *argv[])
mountmode = V2; mountmode = V2;
} else if (strcmp(opt, "nfsv3") == 0) { } else if (strcmp(opt, "nfsv3") == 0) {
mountmode = V3; mountmode = V3;
} else if (strcmp(opt, "nfsv4") == 0) {
pass_flag_to_nmount=0;
mountmode = V4;
fstype = "newnfs";
nfsproto = IPPROTO_TCP;
if (portspec == NULL)
portspec = "2049";
} else if (strcmp(opt, "port") == 0) { } else if (strcmp(opt, "port") == 0) {
pass_flag_to_nmount=0; pass_flag_to_nmount=0;
asprintf(&portspec, "%d", asprintf(&portspec, "%d",
atoi(val)); atoi(val));
if (portspec == NULL) if (portspec == NULL)
err(1, "asprintf"); err(1, "asprintf");
} else if (strcmp(opt, "principal") == 0) {
got_principal = 1;
} else if (strcmp(opt, "sec") == 0) { } else if (strcmp(opt, "sec") == 0) {
/* /*
* Don't add this option to * Don't add this option to
@ -363,6 +381,37 @@ main(int argc, char *argv[])
/* The default is to keep retrying forever. */ /* The default is to keep retrying forever. */
retrycnt = 0; retrycnt = 0;
/*
* If the experimental nfs subsystem is loaded into the kernel
* and the regular one is not, use it. Otherwise, use it if the
* fstype is set to "newnfs", either via "mount -t newnfs ..."
* or by specifying an nfsv4 mount.
*/
if (modfind("nfscl") >= 0 && modfind("nfs") < 0) {
fstype = "newnfs";
} else if (strcmp(fstype, "newnfs") == 0) {
if (modfind("nfscl") < 0) {
/* Not present in kernel, try loading it */
if (kldload("nfscl") < 0 ||
modfind("nfscl") < 0)
errx(1, "nfscl is not available");
}
}
/*
* Add the fqdn to the gssname, as required.
*/
if (gssname != NULL) {
if (strchr(gssname, '@') == NULL &&
gethostname(hostname, MAXHOSTNAMELEN) == 0) {
snprintf(gssn, sizeof (gssn), "%s@%s", gssname,
hostname);
gssname = gssn;
}
build_iovec(&iov, &iovlen, "gssname", gssname,
strlen(gssname) + 1);
}
if (!getnfsargs(spec, &iov, &iovlen)) if (!getnfsargs(spec, &iov, &iovlen))
exit(1); exit(1);
@ -652,7 +701,7 @@ getnfsargs(char *spec, struct iovec **iov, int *iovlen)
int ecode, speclen, remoteerr; int ecode, speclen, remoteerr;
char *hostp, *delimp, *errstr; char *hostp, *delimp, *errstr;
size_t len; size_t len;
static char nam[MNAMELEN + 1]; static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5];
if ((delimp = strrchr(spec, ':')) != NULL) { if ((delimp = strrchr(spec, ':')) != NULL) {
hostp = spec; hostp = spec;
@ -699,7 +748,7 @@ getnfsargs(char *spec, struct iovec **iov, int *iovlen)
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
hints.ai_flags = 0; hints.ai_flags = AI_CANONNAME;
if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
!= 0) { != 0) {
if (portspec == NULL) if (portspec == NULL)
@ -709,6 +758,18 @@ getnfsargs(char *spec, struct iovec **iov, int *iovlen)
gai_strerror(ecode)); gai_strerror(ecode));
return (0); return (0);
} }
/*
* For a Kerberized nfs mount where the "principal"
* argument has not been set, add it here.
*/
if (got_principal == 0 && secflavor >= 0 &&
secflavor != AUTH_SYS && ai_nfs->ai_canonname != NULL) {
snprintf(pname, sizeof (pname), "nfs@%s",
ai_nfs->ai_canonname);
build_iovec(iov, iovlen, "principal", pname,
strlen(pname) + 1);
}
} }
ret = TRYRET_LOCALERR; ret = TRYRET_LOCALERR;
@ -834,7 +895,9 @@ nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr,
} }
tryagain: tryagain:
if (trymntmode == V2) { if (trymntmode == V4) {
nfsvers = 4;
} else if (trymntmode == V2) {
nfsvers = 2; nfsvers = 2;
mntvers = 1; mntvers = 1;
} else { } else {
@ -894,8 +957,7 @@ nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr,
try.tv_sec = 10; try.tv_sec = 10;
try.tv_usec = 0; try.tv_usec = 0;
stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL,
(xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_void, NULL, try);
try);
if (stat != RPC_SUCCESS) { if (stat != RPC_SUCCESS) {
if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
clnt_destroy(clp); clnt_destroy(clp);
@ -910,6 +972,30 @@ nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr,
} }
clnt_destroy(clp); clnt_destroy(clp);
/*
* For NFSv4, there is no mount protocol.
*/
if (trymntmode == V4) {
/*
* Store the server address in nfsargsp, making
* sure to copy any locally allocated structures.
*/
addrlen = nfs_nb.len;
addr = malloc(addrlen);
if (addr == NULL)
err(1, "malloc");
bcopy(nfs_nb.buf, addr, addrlen);
build_iovec(iov, iovlen, "addr", addr, addrlen);
secname = sec_num_to_name(secflavor);
if (secname != NULL)
build_iovec(iov, iovlen, "sec", secname, (size_t)-1);
build_iovec(iov, iovlen, "nfsv4", NULL, 0);
build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1);
return (TRYRET_SUCCESS);
}
/* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */
try.tv_sec = 10; try.tv_sec = 10;
try.tv_usec = 0; try.tv_usec = 0;