aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/super.c
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2008-06-23 12:37:01 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2008-07-09 12:09:29 -0400
commitd8e7748ab8322171ebfd78f6155ff35e7d57ac32 (patch)
treea9efa9e4f871a459d1b8bc3cebab133cb1a378f3 /fs/nfs/super.c
parentce3b7e1906ebbe96753fe090b36de6ffb8e0e0e7 (diff)
NFS: handle interface identifiers in incoming IPv6 addresses
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> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/super.c')
-rw-r--r--fs/nfs/super.c41
1 files changed, 40 insertions, 1 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index d27aa1db0074..73a8e5970f02 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -47,6 +47,7 @@
47#include <linux/inet.h> 47#include <linux/inet.h>
48#include <linux/in6.h> 48#include <linux/in6.h>
49#include <net/ipv6.h> 49#include <net/ipv6.h>
50#include <linux/netdevice.h>
50#include <linux/nfs_xdr.h> 51#include <linux/nfs_xdr.h>
51#include <linux/magic.h> 52#include <linux/magic.h>
52#include <linux/parser.h> 53#include <linux/parser.h>
@@ -725,12 +726,48 @@ static void nfs_parse_ipv4_address(char *string, size_t str_len,
725 *addr_len = 0; 726 *addr_len = 0;
726} 727}
727 728
729#define IPV6_SCOPE_DELIMITER '%'
730
728#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 731#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
732static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
733 const char *delim,
734 struct sockaddr_in6 *sin6)
735{
736 char *p;
737 size_t len;
738
739 if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
740 return ;
741 if (*delim != IPV6_SCOPE_DELIMITER)
742 return;
743
744 len = (string + str_len) - delim - 1;
745 p = kstrndup(delim + 1, len, GFP_KERNEL);
746 if (p) {
747 unsigned long scope_id = 0;
748 struct net_device *dev;
749
750 dev = dev_get_by_name(&init_net, p);
751 if (dev != NULL) {
752 scope_id = dev->ifindex;
753 dev_put(dev);
754 } else {
755 /* scope_id is set to zero on error */
756 strict_strtoul(p, 10, &scope_id);
757 }
758
759 kfree(p);
760 sin6->sin6_scope_id = scope_id;
761 dfprintk(MOUNT, "NFS: IPv6 scope ID = %lu\n", scope_id);
762 }
763}
764
729static void nfs_parse_ipv6_address(char *string, size_t str_len, 765static void nfs_parse_ipv6_address(char *string, size_t str_len,
730 struct sockaddr *sap, size_t *addr_len) 766 struct sockaddr *sap, size_t *addr_len)
731{ 767{
732 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; 768 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
733 u8 *addr = (u8 *)&sin6->sin6_addr.in6_u; 769 u8 *addr = (u8 *)&sin6->sin6_addr.in6_u;
770 const char *delim;
734 771
735 if (str_len <= INET6_ADDRSTRLEN) { 772 if (str_len <= INET6_ADDRSTRLEN) {
736 dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n", 773 dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n",
@@ -738,8 +775,10 @@ static void nfs_parse_ipv6_address(char *string, size_t str_len,
738 775
739 sin6->sin6_family = AF_INET6; 776 sin6->sin6_family = AF_INET6;
740 *addr_len = sizeof(*sin6); 777 *addr_len = sizeof(*sin6);
741 if (in6_pton(string, str_len, addr, '\0', NULL)) 778 if (in6_pton(string, str_len, addr, IPV6_SCOPE_DELIMITER, &delim)) {
779 nfs_parse_ipv6_scope_id(string, str_len, delim, sin6);
742 return; 780 return;
781 }
743 } 782 }
744 783
745 sap->sa_family = AF_UNSPEC; 784 sap->sa_family = AF_UNSPEC;