* [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation
[not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2008-06-17 18:17 ` Chuck Lever
0 siblings, 0 replies; 12+ messages in thread
From: Chuck Lever @ 2008-06-17 18:17 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Traditionally the mount command has looked for a ":" to separate the
server's hostname from the export path in the mounted on device name,
like this:
mount server:/export /mounted/on/dir
The server's hostname is "server" and the export path is "/export".
You can also substitute a specific IPv4 network address for the server
hostname, like this:
mount 192.168.0.55:/export /mounted/on/dir
Raw IPv6 addresses present a problem, however, because they look
something like this:
fe80::200:5aff:fe00:30b
Note the use of colons.
To get around the presence of colons, copy the Solaris convention used for
mounting IPv6 servers by address: wrap a raw IPv6 address with square
brackets.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/super.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 82 insertions(+), 8 deletions(-)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 5e0eefa..98c8110 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1215,14 +1215,9 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
return status;
}
-/*
- * Split "dev_name" into "hostname:export_path".
- *
- * Note: caller frees hostname and export path, even on error.
- */
-static int nfs_parse_devname(const char *dev_name,
- char **hostname, size_t maxnamlen,
- char **export_path, size_t maxpathlen)
+static int nfs_parse_simple_hostname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
{
size_t len;
char *colon, *comma;
@@ -1277,6 +1272,85 @@ out_path:
}
/*
+ * Hostname has square brackets around it because it contains one or
+ * more colons. We look for the first closing square bracket, and a
+ * colon must follow it.
+ */
+static int nfs_parse_protected_hostname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
+{
+ size_t len;
+ char *start, *end;
+
+ start = (char *)(dev_name + 1);
+
+ end = strchr(start, ']');
+ if (end == NULL)
+ goto out_bad_devname;
+ if (*(end + 1) != ':')
+ goto out_bad_devname;
+
+ len = end - start;
+ if (len > maxnamlen)
+ goto out_hostname;
+
+ /* N.B. caller will free nfs_server.hostname in all cases */
+ *hostname = kstrndup(start, len, GFP_KERNEL);
+ if (*hostname == NULL)
+ goto out_nomem;
+
+ end += 2;
+ len = strlen(end);
+ if (len > maxpathlen)
+ goto out_path;
+ *export_path = kstrndup(end, len, GFP_KERNEL);
+ if (!*export_path)
+ goto out_nomem;
+
+ return 0;
+
+out_bad_devname:
+ dfprintk(MOUNT, "NFS: device name not in host:path format\n");
+ return -EINVAL;
+
+out_nomem:
+ dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
+ return -ENOMEM;
+
+out_hostname:
+ dfprintk(MOUNT, "NFS: server hostname too long\n");
+ return -ENAMETOOLONG;
+
+out_path:
+ dfprintk(MOUNT, "NFS: export pathname too long\n");
+ return -ENAMETOOLONG;
+}
+
+/*
+ * Split "dev_name" into "hostname:export_path".
+ *
+ * The leftmost colon demarks the split between the server's hostname
+ * and the export path. If the hostname starts with a left square
+ * bracket, then it may contain colons.
+ *
+ * Note: caller frees hostname and export path, even on error.
+ */
+static int nfs_parse_devname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
+{
+ if (*dev_name == '[')
+ return nfs_parse_protected_hostname(dev_name,
+ hostname, maxnamlen,
+ export_path, maxpathlen);
+
+ return nfs_parse_simple_hostname(dev_name,
+ hostname, maxnamlen,
+ export_path, maxpathlen);
+}
+
+/*
* Validate the NFS2/NFS3 mount data
* - fills in the mount root filehandle
*
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 0/4] NFS mounting with raw IPv6 server hostnames (take 2)
@ 2008-06-18 22:31 Chuck Lever
[not found] ` <20080618222951.16006.3679.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
0 siblings, 1 reply; 12+ messages in thread
From: Chuck Lever @ 2008-06-18 22:31 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Hi Trond-
These four patches introduce the ability to mount NFS servers using a raw
IPv6 address. This support requires the ability to handle colons in the
passed-in server hostname, and the ability to handle an IPv6 interface
identifier in the passed-in server hostname.
IPv6 addresses contain colons. A colon is also used to separate the server
name from the export path. By enclosing an IPv6 address inside square
brackets we effectively escape the colons in the IPv6 address and allow them
to pass unrecognized through the device name parser.
As an added bonus, I also include the patch that changes
nfs_parse_server_address() to take a name length. This allows the passed-in
address string to have no '\0'-termination, which will help us support IPv6
for NFSv4 referrals.
This series corrects the compile problems noted by Stephen Rothwell, allowing
proper "git bisect" operation.
---
Chuck Lever (4):
NFS: handle interface identifiers in incoming IPv6 addresses
NFS: Add string length argument to nfs_parse_server_address
NFS: Support raw IPv6 address hostnames during NFS mount operation
NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3
fs/nfs/super.c | 345 ++++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 270 insertions(+), 75 deletions(-)
--
Chuck Lever
chu ckl eve rat ora cle dot com
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3
[not found] ` <20080618222951.16006.3679.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2008-06-18 22:31 ` Chuck Lever
2008-06-18 22:32 ` [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation Chuck Lever
` (2 subsequent siblings)
3 siblings, 0 replies; 12+ messages in thread
From: Chuck Lever @ 2008-06-18 22:31 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
To support passing a raw IPv6 address as a server hostname, we need to
expand the logic that handles splitting the passed-in device name into
a server hostname and export path
Start by pulling device name parsing out of the mount option validation
functions and into separate helper functions.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/super.c | 124 ++++++++++++++++++++++++++++++++++++--------------------
1 files changed, 79 insertions(+), 45 deletions(-)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index d884f52..5e0eefa 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1216,6 +1216,67 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
}
/*
+ * Split "dev_name" into "hostname:export_path".
+ *
+ * Note: caller frees hostname and export path, even on error.
+ */
+static int nfs_parse_devname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
+{
+ size_t len;
+ char *colon, *comma;
+
+ colon = strchr(dev_name, ':');
+ if (colon == NULL)
+ goto out_bad_devname;
+
+ len = colon - dev_name;
+ if (len > maxnamlen)
+ goto out_hostname;
+
+ /* N.B. caller will free nfs_server.hostname in all cases */
+ *hostname = kstrndup(dev_name, len, GFP_KERNEL);
+ if (!*hostname)
+ goto out_nomem;
+
+ /* kill possible hostname list: not supported */
+ comma = strchr(*hostname, ',');
+ if (comma != NULL) {
+ if (comma == *hostname)
+ goto out_bad_devname;
+ *comma = '\0';
+ }
+
+ colon++;
+ len = strlen(colon);
+ if (len > maxpathlen)
+ goto out_path;
+ *export_path = kstrndup(colon, len, GFP_KERNEL);
+ if (!*export_path)
+ goto out_nomem;
+
+ dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *export_path);
+ return 0;
+
+out_bad_devname:
+ dfprintk(MOUNT, "NFS: device name not in host:path format\n");
+ return -EINVAL;
+
+out_nomem:
+ dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
+ return -ENOMEM;
+
+out_hostname:
+ dfprintk(MOUNT, "NFS: server hostname too long\n");
+ return -ENAMETOOLONG;
+
+out_path:
+ dfprintk(MOUNT, "NFS: export pathname too long\n");
+ return -ENAMETOOLONG;
+}
+
+/*
* Validate the NFS2/NFS3 mount data
* - fills in the mount root filehandle
*
@@ -1341,8 +1402,6 @@ static int nfs_validate_mount_data(void *options,
break;
default: {
- unsigned int len;
- char *c;
int status;
if (nfs_parse_mount_options((char *)options, args) == 0)
@@ -1357,21 +1416,17 @@ static int nfs_validate_mount_data(void *options,
nfs_set_transport_defaults(args);
- c = strchr(dev_name, ':');
- if (c == NULL)
- return -EINVAL;
- len = c - dev_name;
- /* N.B. caller will free nfs_server.hostname in all cases */
- args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL);
- if (!args->nfs_server.hostname)
- goto out_nomem;
+ status = nfs_parse_devname(dev_name,
+ &args->nfs_server.hostname,
+ PAGE_SIZE,
+ &args->nfs_server.export_path,
+ NFS_MAXPATHLEN);
+ if (!status)
+ status = nfs_try_mount(args, mntfh);
- c++;
- if (strlen(c) > NFS_MAXPATHLEN)
- return -ENAMETOOLONG;
- args->nfs_server.export_path = c;
+ kfree(args->nfs_server.export_path);
+ args->nfs_server.export_path = NULL;
- status = nfs_try_mount(args, mntfh);
if (status)
return status;
@@ -1898,7 +1953,7 @@ static int nfs4_validate_mount_data(void *options,
break;
default: {
- unsigned int len;
+ int status;
if (nfs_parse_mount_options((char *)options, args) == 0)
return -EINVAL;
@@ -1922,34 +1977,17 @@ static int nfs4_validate_mount_data(void *options,
goto out_inval_auth;
}
- /*
- * Split "dev_name" into "hostname:mntpath".
- */
- c = strchr(dev_name, ':');
- if (c == NULL)
- return -EINVAL;
- /* while calculating len, pretend ':' is '\0' */
- len = c - dev_name;
- if (len > NFS4_MAXNAMLEN)
- return -ENAMETOOLONG;
- /* N.B. caller will free nfs_server.hostname in all cases */
- args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL);
- if (!args->nfs_server.hostname)
- goto out_nomem;
-
- c++; /* step over the ':' */
- len = strlen(c);
- if (len > NFS4_MAXPATHLEN)
- return -ENAMETOOLONG;
- args->nfs_server.export_path = kstrndup(c, len, GFP_KERNEL);
- if (!args->nfs_server.export_path)
- goto out_nomem;
-
- dprintk("NFS: MNTPATH: '%s'\n", args->nfs_server.export_path);
-
if (args->client_address == NULL)
goto out_no_client_address;
+ status = nfs_parse_devname(dev_name,
+ &args->nfs_server.hostname,
+ NFS4_MAXNAMLEN,
+ &args->nfs_server.export_path,
+ NFS4_MAXPATHLEN);
+ if (status < 0)
+ return status;
+
break;
}
}
@@ -1965,10 +2003,6 @@ out_inval_auth:
data->auth_flavourlen);
return -EINVAL;
-out_nomem:
- dfprintk(MOUNT, "NFS4: not enough memory to handle mount options\n");
- return -ENOMEM;
-
out_no_address:
dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
return -EINVAL;
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation
[not found] ` <20080618222951.16006.3679.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-18 22:31 ` [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 Chuck Lever
@ 2008-06-18 22:32 ` Chuck Lever
[not found] ` <20080618223203.16006.61765.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-18 22:32 ` [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address Chuck Lever
2008-06-18 22:32 ` [PATCH 4/4] NFS: handle interface identifiers in incoming IPv6 addresses Chuck Lever
3 siblings, 1 reply; 12+ messages in thread
From: Chuck Lever @ 2008-06-18 22:32 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Traditionally the mount command has looked for a ":" to separate the
server's hostname from the export path in the mounted on device name,
like this:
mount server:/export /mounted/on/dir
The server's hostname is "server" and the export path is "/export".
You can also substitute a specific IPv4 network address for the server
hostname, like this:
mount 192.168.0.55:/export /mounted/on/dir
Raw IPv6 addresses present a problem, however, because they look
something like this:
fe80::200:5aff:fe00:30b
Note the use of colons.
To get around the presence of colons, copy the Solaris convention used for
mounting IPv6 servers by address: wrap a raw IPv6 address with square
brackets.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/super.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 82 insertions(+), 8 deletions(-)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 5e0eefa..98c8110 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1215,14 +1215,9 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
return status;
}
-/*
- * Split "dev_name" into "hostname:export_path".
- *
- * Note: caller frees hostname and export path, even on error.
- */
-static int nfs_parse_devname(const char *dev_name,
- char **hostname, size_t maxnamlen,
- char **export_path, size_t maxpathlen)
+static int nfs_parse_simple_hostname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
{
size_t len;
char *colon, *comma;
@@ -1277,6 +1272,85 @@ out_path:
}
/*
+ * Hostname has square brackets around it because it contains one or
+ * more colons. We look for the first closing square bracket, and a
+ * colon must follow it.
+ */
+static int nfs_parse_protected_hostname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
+{
+ size_t len;
+ char *start, *end;
+
+ start = (char *)(dev_name + 1);
+
+ end = strchr(start, ']');
+ if (end == NULL)
+ goto out_bad_devname;
+ if (*(end + 1) != ':')
+ goto out_bad_devname;
+
+ len = end - start;
+ if (len > maxnamlen)
+ goto out_hostname;
+
+ /* N.B. caller will free nfs_server.hostname in all cases */
+ *hostname = kstrndup(start, len, GFP_KERNEL);
+ if (*hostname == NULL)
+ goto out_nomem;
+
+ end += 2;
+ len = strlen(end);
+ if (len > maxpathlen)
+ goto out_path;
+ *export_path = kstrndup(end, len, GFP_KERNEL);
+ if (!*export_path)
+ goto out_nomem;
+
+ return 0;
+
+out_bad_devname:
+ dfprintk(MOUNT, "NFS: device name not in host:path format\n");
+ return -EINVAL;
+
+out_nomem:
+ dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
+ return -ENOMEM;
+
+out_hostname:
+ dfprintk(MOUNT, "NFS: server hostname too long\n");
+ return -ENAMETOOLONG;
+
+out_path:
+ dfprintk(MOUNT, "NFS: export pathname too long\n");
+ return -ENAMETOOLONG;
+}
+
+/*
+ * Split "dev_name" into "hostname:export_path".
+ *
+ * The leftmost colon demarks the split between the server's hostname
+ * and the export path. If the hostname starts with a left square
+ * bracket, then it may contain colons.
+ *
+ * Note: caller frees hostname and export path, even on error.
+ */
+static int nfs_parse_devname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
+{
+ if (*dev_name == '[')
+ return nfs_parse_protected_hostname(dev_name,
+ hostname, maxnamlen,
+ export_path, maxpathlen);
+
+ return nfs_parse_simple_hostname(dev_name,
+ hostname, maxnamlen,
+ export_path, maxpathlen);
+}
+
+/*
* Validate the NFS2/NFS3 mount data
* - fills in the mount root filehandle
*
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address
[not found] ` <20080618222951.16006.3679.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-18 22:31 ` [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 Chuck Lever
2008-06-18 22:32 ` [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation Chuck Lever
@ 2008-06-18 22:32 ` Chuck Lever
[not found] ` <20080618223211.16006.8247.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-18 22:32 ` [PATCH 4/4] NFS: handle interface identifiers in incoming IPv6 addresses Chuck Lever
3 siblings, 1 reply; 12+ messages in thread
From: Chuck Lever @ 2008-06-18 22:32 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
To make nfs_parse_server_address() more generally useful, allow it to
accept input strings that are not terminated with '\0'.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/super.c | 107 ++++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 77 insertions(+), 30 deletions(-)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 98c8110..818bc1b 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -62,6 +62,13 @@
#define NFSDBG_FACILITY NFSDBG_VFS
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN (16)
+#endif
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN (48)
+#endif
+
enum {
/* Mount options that take no arguments */
Opt_soft, Opt_hard,
@@ -702,38 +709,76 @@ static int nfs_verify_server_address(struct sockaddr *addr)
return 0;
}
-/*
- * Parse string addresses passed in via a mount option,
- * and construct a sockaddr based on the result.
- *
- * If address parsing fails, set the sockaddr's address
- * family to AF_UNSPEC to force nfs_verify_server_address()
- * to punt the mount.
- */
-static void nfs_parse_server_address(char *value,
- struct sockaddr *sap,
- size_t *len)
+static void nfs_parse_ipv4_address(char *string, size_t str_len,
+ struct sockaddr *sap, size_t *addr_len)
{
- if (strchr(value, ':')) {
- struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap;
- u8 *addr = (u8 *)&ap->sin6_addr.in6_u;
+ struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+ u8 *addr = (u8 *)&sin->sin_addr.s_addr;
+
+ if (str_len <= INET_ADDRSTRLEN) {
+ dfprintk(MOUNT, "NFS: parsing IPv4 address %*s\n",
+ (int)str_len, string);
- ap->sin6_family = AF_INET6;
- *len = sizeof(*ap);
- if (in6_pton(value, -1, addr, '\0', NULL))
+ sin->sin_family = AF_INET;
+ *addr_len = sizeof(*sin);
+ if (in4_pton(string, str_len, addr, '\0', NULL))
return;
- } else {
- struct sockaddr_in *ap = (struct sockaddr_in *)sap;
- u8 *addr = (u8 *)&ap->sin_addr.s_addr;
+ }
+
+ sap->sa_family = AF_UNSPEC;
+ *addr_len = 0;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static void nfs_parse_ipv6_address(char *string, size_t str_len,
+ struct sockaddr *sap, size_t *addr_len)
+{
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+ u8 *addr = (u8 *)&sin6->sin6_addr.in6_u;
- ap->sin_family = AF_INET;
- *len = sizeof(*ap);
- if (in4_pton(value, -1, addr, '\0', NULL))
+ if (str_len <= INET6_ADDRSTRLEN) {
+ dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n",
+ (int)str_len, string);
+
+ sin6->sin6_family = AF_INET6;
+ *addr_len = sizeof(*sin6);
+ if (in6_pton(string, str_len, addr, '\0', NULL))
return;
}
sap->sa_family = AF_UNSPEC;
- *len = 0;
+ *addr_len = 0;
+}
+#else
+static void nfs_parse_ipv6_address(char *string, size_t str_len,
+ struct sockaddr *sap, size_t *addr_len)
+{
+ sap->sa_family = AF_UNSPEC;
+ *addr_len = 0;
+}
+#endif
+
+/*
+ * Construct a sockaddr based on the contents of a string that contains
+ * an IP address in presentation format.
+ *
+ * If there is a problem constructing the new sockaddr, set the address
+ * family to AF_UNSPEC.
+ */
+static void nfs_parse_ip_address(char *string, size_t str_len,
+ struct sockaddr *sap, size_t *addr_len)
+{
+ unsigned int i, colons;
+
+ colons = 0;
+ for (i = 0; i < str_len; i++)
+ if (string[i] == ':')
+ colons++;
+
+ if (colons >= 2)
+ nfs_parse_ipv6_address(string, str_len, sap, addr_len);
+ else
+ nfs_parse_ipv4_address(string, str_len, sap, addr_len);
}
/*
@@ -1093,9 +1138,10 @@ static int nfs_parse_mount_options(char *raw,
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
- nfs_parse_server_address(string, (struct sockaddr *)
- &mnt->nfs_server.address,
- &mnt->nfs_server.addrlen);
+ nfs_parse_ip_address(string, strlen(string),
+ (struct sockaddr *)
+ &mnt->nfs_server.address,
+ &mnt->nfs_server.addrlen);
kfree(string);
break;
case Opt_clientaddr:
@@ -1116,9 +1162,10 @@ static int nfs_parse_mount_options(char *raw,
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
- nfs_parse_server_address(string, (struct sockaddr *)
- &mnt->mount_server.address,
- &mnt->mount_server.addrlen);
+ nfs_parse_ip_address(string, strlen(string),
+ (struct sockaddr *)
+ &mnt->mount_server.address,
+ &mnt->mount_server.addrlen);
kfree(string);
break;
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 4/4] NFS: handle interface identifiers in incoming IPv6 addresses
[not found] ` <20080618222951.16006.3679.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
` (2 preceding siblings ...)
2008-06-18 22:32 ` [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address Chuck Lever
@ 2008-06-18 22:32 ` Chuck Lever
3 siblings, 0 replies; 12+ messages in thread
From: Chuck Lever @ 2008-06-18 22:32 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Add support in the kernel NFS client's address parser for interface
identifiers.
IPv6 link-local addresses require an additional "interface identifier",
which is a network device name or an integer that indexes the array of
local network interfaces. They are suffixed to the address with a '%'.
For example:
fe80::215:c5ff:fe3b:e1b2%2
indicates an interface index of 2. Or
fe80::215:c5ff:fe3b:e1b2%eth0
indicates that requests should be routed through the eth0 device.
Without the interface ID, link-local addresses are not usable for NFS.
Both the kernel NFS client mount option parser and the mount.nfs command
can take either form. The mount.nfs command always passes the address
through getnameinfo(3), which usually re-writes interface indices as
device names.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/super.c | 42 +++++++++++++++++++++++++++++++++++++++++-
1 files changed, 41 insertions(+), 1 deletions(-)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 818bc1b..2bb1756 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -47,6 +47,7 @@
#include <linux/inet.h>
#include <linux/in6.h>
#include <net/ipv6.h>
+#include <linux/netdevice.h>
#include <linux/nfs_xdr.h>
#include <linux/magic.h>
#include <linux/parser.h>
@@ -68,6 +69,9 @@
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN (48)
#endif
+#ifndef SCOPE_DELIMITER
+#define SCOPE_DELIMITER '%'
+#endif
enum {
/* Mount options that take no arguments */
@@ -730,11 +734,45 @@ static void nfs_parse_ipv4_address(char *string, size_t str_len,
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
+ const char *delim,
+ struct sockaddr_in6 *sin6)
+{
+ char *p;
+ size_t len;
+
+ if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
+ return ;
+ if (*delim != SCOPE_DELIMITER)
+ return;
+
+ len = (string + str_len) - delim - 1;
+ p = kstrndup(delim + 1, len, GFP_KERNEL);
+ if (p) {
+ unsigned long scope_id = 0;
+ struct net_device *dev;
+
+ dev = dev_get_by_name(&init_net, p);
+ if (dev != NULL) {
+ scope_id = dev->ifindex;
+ dev_put(dev);
+ } else {
+ /* scope_id is set to zero on error */
+ strict_strtoul(p, 10, &scope_id);
+ }
+
+ kfree(p);
+ sin6->sin6_scope_id = scope_id;
+ dfprintk(MOUNT, "NFS: IPv6 scope ID = %lu\n", scope_id);
+ }
+}
+
static void nfs_parse_ipv6_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
u8 *addr = (u8 *)&sin6->sin6_addr.in6_u;
+ const char *delim;
if (str_len <= INET6_ADDRSTRLEN) {
dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n",
@@ -742,8 +780,10 @@ static void nfs_parse_ipv6_address(char *string, size_t str_len,
sin6->sin6_family = AF_INET6;
*addr_len = sizeof(*sin6);
- if (in6_pton(string, str_len, addr, '\0', NULL))
+ if (in6_pton(string, str_len, addr, SCOPE_DELIMITER, &delim)) {
+ nfs_parse_ipv6_scope_id(string, str_len, delim, sin6);
return;
+ }
}
sap->sa_family = AF_UNSPEC;
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation
[not found] ` <20080618223203.16006.61765.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2008-06-18 22:35 ` Peter Staubach
2008-06-18 22:42 ` Chuck Lever
0 siblings, 1 reply; 12+ messages in thread
From: Peter Staubach @ 2008-06-18 22:35 UTC (permalink / raw)
To: Chuck Lever; +Cc: trond.myklebust, linux-nfs
Chuck Lever wrote:
> Traditionally the mount command has looked for a ":" to separate the
> server's hostname from the export path in the mounted on device name,
> like this:
>
> mount server:/export /mounted/on/dir
>
> The server's hostname is "server" and the export path is "/export".
>
> You can also substitute a specific IPv4 network address for the server
> hostname, like this:
>
> mount 192.168.0.55:/export /mounted/on/dir
>
> Raw IPv6 addresses present a problem, however, because they look
> something like this:
>
> fe80::200:5aff:fe00:30b
>
> Note the use of colons.
>
> To get around the presence of colons, copy the Solaris convention used for
> mounting IPv6 servers by address: wrap a raw IPv6 address with square
> brackets.
>
>
It seems unfortunate that the convention couldn't have been to
look for the first instance of ":/" and break the strings there.
ps
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> ---
>
> fs/nfs/super.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
> 1 files changed, 82 insertions(+), 8 deletions(-)
>
>
> diff --git a/fs/nfs/super.c b/fs/nfs/super.c
> index 5e0eefa..98c8110 100644
> --- a/fs/nfs/super.c
> +++ b/fs/nfs/super.c
> @@ -1215,14 +1215,9 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
> return status;
> }
>
> -/*
> - * Split "dev_name" into "hostname:export_path".
> - *
> - * Note: caller frees hostname and export path, even on error.
> - */
> -static int nfs_parse_devname(const char *dev_name,
> - char **hostname, size_t maxnamlen,
> - char **export_path, size_t maxpathlen)
> +static int nfs_parse_simple_hostname(const char *dev_name,
> + char **hostname, size_t maxnamlen,
> + char **export_path, size_t maxpathlen)
> {
> size_t len;
> char *colon, *comma;
> @@ -1277,6 +1272,85 @@ out_path:
> }
>
> /*
> + * Hostname has square brackets around it because it contains one or
> + * more colons. We look for the first closing square bracket, and a
> + * colon must follow it.
> + */
> +static int nfs_parse_protected_hostname(const char *dev_name,
> + char **hostname, size_t maxnamlen,
> + char **export_path, size_t maxpathlen)
> +{
> + size_t len;
> + char *start, *end;
> +
> + start = (char *)(dev_name + 1);
> +
> + end = strchr(start, ']');
> + if (end == NULL)
> + goto out_bad_devname;
> + if (*(end + 1) != ':')
> + goto out_bad_devname;
> +
> + len = end - start;
> + if (len > maxnamlen)
> + goto out_hostname;
> +
> + /* N.B. caller will free nfs_server.hostname in all cases */
> + *hostname = kstrndup(start, len, GFP_KERNEL);
> + if (*hostname == NULL)
> + goto out_nomem;
> +
> + end += 2;
> + len = strlen(end);
> + if (len > maxpathlen)
> + goto out_path;
> + *export_path = kstrndup(end, len, GFP_KERNEL);
> + if (!*export_path)
> + goto out_nomem;
> +
> + return 0;
> +
> +out_bad_devname:
> + dfprintk(MOUNT, "NFS: device name not in host:path format\n");
> + return -EINVAL;
> +
> +out_nomem:
> + dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
> + return -ENOMEM;
> +
> +out_hostname:
> + dfprintk(MOUNT, "NFS: server hostname too long\n");
> + return -ENAMETOOLONG;
> +
> +out_path:
> + dfprintk(MOUNT, "NFS: export pathname too long\n");
> + return -ENAMETOOLONG;
> +}
> +
> +/*
> + * Split "dev_name" into "hostname:export_path".
> + *
> + * The leftmost colon demarks the split between the server's hostname
> + * and the export path. If the hostname starts with a left square
> + * bracket, then it may contain colons.
> + *
> + * Note: caller frees hostname and export path, even on error.
> + */
> +static int nfs_parse_devname(const char *dev_name,
> + char **hostname, size_t maxnamlen,
> + char **export_path, size_t maxpathlen)
> +{
> + if (*dev_name == '[')
> + return nfs_parse_protected_hostname(dev_name,
> + hostname, maxnamlen,
> + export_path, maxpathlen);
> +
> + return nfs_parse_simple_hostname(dev_name,
> + hostname, maxnamlen,
> + export_path, maxpathlen);
> +}
> +
> +/*
> * Validate the NFS2/NFS3 mount data
> * - fills in the mount root filehandle
> *
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation
2008-06-18 22:35 ` Peter Staubach
@ 2008-06-18 22:42 ` Chuck Lever
2008-06-18 22:51 ` Trond Myklebust
0 siblings, 1 reply; 12+ messages in thread
From: Chuck Lever @ 2008-06-18 22:42 UTC (permalink / raw)
To: Peter Staubach; +Cc: trond.myklebust, linux-nfs
[-- Attachment #1: Type: text/plain, Size: 1359 bytes --]
Peter Staubach wrote:
> Chuck Lever wrote:
>> Traditionally the mount command has looked for a ":" to separate the
>> server's hostname from the export path in the mounted on device name,
>> like this:
>>
>> mount server:/export /mounted/on/dir
>>
>> The server's hostname is "server" and the export path is "/export".
>>
>> You can also substitute a specific IPv4 network address for the server
>> hostname, like this:
>>
>> mount 192.168.0.55:/export /mounted/on/dir
>>
>> Raw IPv6 addresses present a problem, however, because they look
>> something like this:
>>
>> fe80::200:5aff:fe00:30b
>>
>> Note the use of colons.
>>
>> To get around the presence of colons, copy the Solaris convention used
>> for
>> mounting IPv6 servers by address: wrap a raw IPv6 address with square
>> brackets.
>>
>>
>
> It seems unfortunate that the convention couldn't have been to
> look for the first instance of ":/" and break the strings there.
I wonder if you can ever specify an export path that does not begin with
a '/' -- is that possible when mounting an NFS server that exports, say,
an old-fashioned HFS volume?
"macintosh::Macintosh HD:Users:cel"
Looking for only the first colon gives the correct results in this case,
but looking for ":/" would not.
Likewise on FAT32:
"windows:\Documents and Settings\Chuck Lever\My Documents"
[-- Attachment #2: chuck_lever.vcf --]
[-- Type: text/x-vcard, Size: 259 bytes --]
begin:vcard
fn:Chuck Lever
n:Lever;Chuck
org:Oracle Corporation;Corporate Architecture: Linux Projects Group
adr:;;1015 Granger Avenue;Ann Arbor;MI;48104;USA
title:Principal Member of Staff
tel;work:+1 248 614 5091
x-mozilla-html:FALSE
version:2.1
end:vcard
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation
2008-06-18 22:42 ` Chuck Lever
@ 2008-06-18 22:51 ` Trond Myklebust
2008-06-18 22:59 ` Peter Staubach
0 siblings, 1 reply; 12+ messages in thread
From: Trond Myklebust @ 2008-06-18 22:51 UTC (permalink / raw)
To: chuck.lever; +Cc: Peter Staubach, linux-nfs
On Wed, 2008-06-18 at 18:42 -0400, Chuck Lever wrote:
> Peter Staubach wrote:
> > It seems unfortunate that the convention couldn't have been to
> > look for the first instance of ":/" and break the strings there.
>
> I wonder if you can ever specify an export path that does not begin with
> a '/' -- is that possible when mounting an NFS server that exports, say,
> an old-fashioned HFS volume?
It is quite legal to specify a path that doesn't start with '/':
NFSv2/v3 mount servers will, for instance, just implicitly assume it.
For that reason, there were user complaints the last time we started
requiring the '/' and returning an error when it wasn't supplied.
--
Trond Myklebust
Linux NFS client maintainer
NetApp
Trond.Myklebust@netapp.com
www.netapp.com
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation
2008-06-18 22:51 ` Trond Myklebust
@ 2008-06-18 22:59 ` Peter Staubach
0 siblings, 0 replies; 12+ messages in thread
From: Peter Staubach @ 2008-06-18 22:59 UTC (permalink / raw)
To: Trond Myklebust; +Cc: chuck.lever, linux-nfs
Trond Myklebust wrote:
> On Wed, 2008-06-18 at 18:42 -0400, Chuck Lever wrote:
>
>> Peter Staubach wrote:
>>
>>> It seems unfortunate that the convention couldn't have been to
>>> look for the first instance of ":/" and break the strings there.
>>>
>> I wonder if you can ever specify an export path that does not begin with
>> a '/' -- is that possible when mounting an NFS server that exports, say,
>> an old-fashioned HFS volume?
>>
>
> It is quite legal to specify a path that doesn't start with '/':
> NFSv2/v3 mount servers will, for instance, just implicitly assume it.
> For that reason, there were user complaints the last time we started
> requiring the '/' and returning an error when it wasn't supplied.
And, hence, the [] syntax... :-)
ps
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address
[not found] ` <20080618223211.16006.8247.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2008-06-19 17:43 ` Trond Myklebust
0 siblings, 0 replies; 12+ messages in thread
From: Trond Myklebust @ 2008-06-19 17:43 UTC (permalink / raw)
To: Chuck Lever; +Cc: linux-nfs
On Wed, 2008-06-18 at 18:32 -0400, Chuck Lever wrote:
> To make nfs_parse_server_address() more generally useful, allow it to
> accept input strings that are not terminated with '\0'.
>
> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
> ---
>
> fs/nfs/super.c | 107 ++++++++++++++++++++++++++++++++++++++++----------------
> 1 files changed, 77 insertions(+), 30 deletions(-)
>
>
> diff --git a/fs/nfs/super.c b/fs/nfs/super.c
> index 98c8110..818bc1b 100644
> --- a/fs/nfs/super.c
> +++ b/fs/nfs/super.c
> @@ -62,6 +62,13 @@
>
> #define NFSDBG_FACILITY NFSDBG_VFS
>
> +#ifndef INET_ADDRSTRLEN
> +#define INET_ADDRSTRLEN (16)
> +#endif
> +#ifndef INET6_ADDRSTRLEN
> +#define INET6_ADDRSTRLEN (48)
> +#endif
> +
If you're resending, then can you please get rid of the above #ifndefs?
If somebody else is defining an INET_ADDRSTRLEN that overrides ours,
then I definitely want to know about it at compile time, so that I can
check for correctness.
--
Trond Myklebust
Linux NFS client maintainer
NetApp
Trond.Myklebust@netapp.com
www.netapp.com
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation
[not found] ` <20080623163129.10539.15565.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2008-06-23 16:36 ` Chuck Lever
0 siblings, 0 replies; 12+ messages in thread
From: Chuck Lever @ 2008-06-23 16:36 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Traditionally the mount command has looked for a ":" to separate the
server's hostname from the export path in the mounted on device name,
like this:
mount server:/export /mounted/on/dir
The server's hostname is "server" and the export path is "/export".
You can also substitute a specific IPv4 network address for the server
hostname, like this:
mount 192.168.0.55:/export /mounted/on/dir
Raw IPv6 addresses present a problem, however, because they look
something like this:
fe80::200:5aff:fe00:30b
Note the use of colons.
To get around the presence of colons, copy the Solaris convention used for
mounting IPv6 servers by address: wrap a raw IPv6 address with square
brackets.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/super.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 82 insertions(+), 8 deletions(-)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 5e0eefa..98c8110 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1215,14 +1215,9 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
return status;
}
-/*
- * Split "dev_name" into "hostname:export_path".
- *
- * Note: caller frees hostname and export path, even on error.
- */
-static int nfs_parse_devname(const char *dev_name,
- char **hostname, size_t maxnamlen,
- char **export_path, size_t maxpathlen)
+static int nfs_parse_simple_hostname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
{
size_t len;
char *colon, *comma;
@@ -1277,6 +1272,85 @@ out_path:
}
/*
+ * Hostname has square brackets around it because it contains one or
+ * more colons. We look for the first closing square bracket, and a
+ * colon must follow it.
+ */
+static int nfs_parse_protected_hostname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
+{
+ size_t len;
+ char *start, *end;
+
+ start = (char *)(dev_name + 1);
+
+ end = strchr(start, ']');
+ if (end == NULL)
+ goto out_bad_devname;
+ if (*(end + 1) != ':')
+ goto out_bad_devname;
+
+ len = end - start;
+ if (len > maxnamlen)
+ goto out_hostname;
+
+ /* N.B. caller will free nfs_server.hostname in all cases */
+ *hostname = kstrndup(start, len, GFP_KERNEL);
+ if (*hostname == NULL)
+ goto out_nomem;
+
+ end += 2;
+ len = strlen(end);
+ if (len > maxpathlen)
+ goto out_path;
+ *export_path = kstrndup(end, len, GFP_KERNEL);
+ if (!*export_path)
+ goto out_nomem;
+
+ return 0;
+
+out_bad_devname:
+ dfprintk(MOUNT, "NFS: device name not in host:path format\n");
+ return -EINVAL;
+
+out_nomem:
+ dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
+ return -ENOMEM;
+
+out_hostname:
+ dfprintk(MOUNT, "NFS: server hostname too long\n");
+ return -ENAMETOOLONG;
+
+out_path:
+ dfprintk(MOUNT, "NFS: export pathname too long\n");
+ return -ENAMETOOLONG;
+}
+
+/*
+ * Split "dev_name" into "hostname:export_path".
+ *
+ * The leftmost colon demarks the split between the server's hostname
+ * and the export path. If the hostname starts with a left square
+ * bracket, then it may contain colons.
+ *
+ * Note: caller frees hostname and export path, even on error.
+ */
+static int nfs_parse_devname(const char *dev_name,
+ char **hostname, size_t maxnamlen,
+ char **export_path, size_t maxpathlen)
+{
+ if (*dev_name == '[')
+ return nfs_parse_protected_hostname(dev_name,
+ hostname, maxnamlen,
+ export_path, maxpathlen);
+
+ return nfs_parse_simple_hostname(dev_name,
+ hostname, maxnamlen,
+ export_path, maxpathlen);
+}
+
+/*
* Validate the NFS2/NFS3 mount data
* - fills in the mount root filehandle
*
^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2008-06-23 16:37 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-18 22:31 [PATCH 0/4] NFS mounting with raw IPv6 server hostnames (take 2) Chuck Lever
[not found] ` <20080618222951.16006.3679.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-18 22:31 ` [PATCH 1/4] NFS: Use common device name parsing logic for NFSv4 and NFSv2/v3 Chuck Lever
2008-06-18 22:32 ` [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation Chuck Lever
[not found] ` <20080618223203.16006.61765.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-18 22:35 ` Peter Staubach
2008-06-18 22:42 ` Chuck Lever
2008-06-18 22:51 ` Trond Myklebust
2008-06-18 22:59 ` Peter Staubach
2008-06-18 22:32 ` [PATCH 3/4] NFS: Add string length argument to nfs_parse_server_address Chuck Lever
[not found] ` <20080618223211.16006.8247.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-19 17:43 ` Trond Myklebust
2008-06-18 22:32 ` [PATCH 4/4] NFS: handle interface identifiers in incoming IPv6 addresses Chuck Lever
-- strict thread matches above, loose matches on Subject: below --
2008-06-23 16:36 [PATCH 0/4] NFS mounting with raw IPv6 server hostnames (take 3) Chuck Lever
[not found] ` <20080623163129.10539.15565.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-23 16:36 ` [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation Chuck Lever
2008-06-17 18:17 [PATCH 0/4] NFS mounting with raw IPv6 server hostnames Chuck Lever
[not found] ` <20080617181622.3215.61295.stgit-ewv44WTpT0t9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2008-06-17 18:17 ` [PATCH 2/4] NFS: Support raw IPv6 address hostnames during NFS mount operation Chuck Lever
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox