diff mbox

[linux-cifs-client,4/5] cifs: have cifs parse scope_id out of IPv6 addresses and use it

Message ID 1244730452-19704-5-git-send-email-jlayton@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jeff Layton June 11, 2009, 2:27 p.m. UTC
This patch has CIFS look for a '%' in an IPv6 address. If one is
present then it will try to treat that value as a numeric interface
index suitable for stuffing into the sin6_scope_id field.

This should allow people to mount servers on IPv6 link-local addresses.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/cifs/connect.c     |    6 ++++--
 fs/cifs/dns_resolve.c |    4 ++--
 fs/cifs/netmisc.c     |   34 ++++++++++++++++++++++++++++------
 3 files changed, 34 insertions(+), 10 deletions(-)

Comments

David Holder June 11, 2009, 3:13 p.m. UTC | #1
Jeff,

Great to see this going into the code. Do you plan to allow for textual
interface names after the %?

You can use getnameinfo() with the NI_NUMERICHOST flag to convert the
textual interface name and IPv6 address to numeric format or just use
if_nametoindex().

Regards, David

> This patch has CIFS look for a '%' in an IPv6 address. If one is
> present then it will try to treat that value as a numeric interface
> index suitable for stuffing into the sin6_scope_id field.
>
> This should allow people to mount servers on IPv6 link-local addresses.
>
> Signed-off-by: Jeff Layton <jlayton@redhat.com>
> ---
>  fs/cifs/connect.c     |    6 ++++--
>  fs/cifs/dns_resolve.c |    4 ++--
>  fs/cifs/netmisc.c     |   34 ++++++++++++++++++++++++++++------
>  3 files changed, 34 insertions(+), 10 deletions(-)
>
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 6a91b5f..c9e489c 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -1374,8 +1374,10 @@ cifs_find_tcp_session(struct sockaddr_storage
> *addr)
>  		     server->addr.sockAddr.sin_addr.s_addr))
>  			continue;
>  		else if (addr->ss_family == AF_INET6 &&
> -			 !ipv6_addr_equal(&server->addr.sockAddr6.sin6_addr,
> -					  &addr6->sin6_addr))
> +			 (!ipv6_addr_equal(&server->addr.sockAddr6.sin6_addr,
> +					   &addr6->sin6_addr) ||
> +			  server->addr.sockAddr6.sin6_scope_id !=
> +					   addr6->sin6_scope_id))
>  			continue;
>
>  		++server->srv_count;
> diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
> index 91b5500..8794814 100644
> --- a/fs/cifs/dns_resolve.c
> +++ b/fs/cifs/dns_resolve.c
> @@ -35,7 +35,7 @@
>   * 		0 - name is not IP
>   */
>  static int
> -is_ip(const char *name)
> +is_ip(char *name)
>  {
>  	struct sockaddr_storage ss;
>
> @@ -57,7 +57,7 @@ dns_resolver_instantiate(struct key *key, const void
> *data,
>  	ip[datalen] = '\0';
>
>  	/* make sure this looks like an address */
> -	if (!is_ip((const char *) ip)) {
> +	if (!is_ip(ip)) {
>  		kfree(ip);
>  		return -EINVAL;
>  	}
> diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
> index 00e6e35..bd6d689 100644
> --- a/fs/cifs/netmisc.c
> +++ b/fs/cifs/netmisc.c
> @@ -158,25 +158,47 @@ cifs_inet_pton(const int address_family, const char
> *cp, void *dst)
>  /*
>   * Try to convert a string to an IPv4 address and then attempt to convert
>   * it to an IPv6 address if that fails. Set the family field if either
> - * succeeds.
> + * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to
> + * treat the part following it as a numeric sin6_scope_id.
>   *
>   * Returns 0 on failure.
>   */
>  int
>  cifs_convert_address(char *src, void *dst)
>  {
> +	int rc;
> +	char *pct, *endp;
>  	struct sockaddr_in *s4 = (struct sockaddr_in *) dst;
> -	struct sockaddr_in6 *s6 = (Struct sockaddr_in6 *) dst;
> +	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst;
>
> +	/* IPv4 address */
>  	if (cifs_inet_pton(AF_INET, src, &s4->sin_addr.s_addr)) {
>  		s4->sin_family = AF_INET;
>  		return 1;
> -	} else if (cifs_inet_pton(AF_INET6, src, &s6->sin6_addr.s6_addr)) {
> -		s6->sin6_family = AF_INET6;
> -		return 1;
>  	}
>
> -	return 0;
> +	/* temporarily terminate string */
> +	pct = strchr(src, '%');
> +	if (pct)
> +		*pct = '\0';
> +
> +	rc = cifs_inet_pton(AF_INET6, src, &s6->sin6_addr.s6_addr);
> +
> +	/* repair temp termination (if any) and make pct point to scopeid */
> +	if (pct)
> +		*pct++ = '%';
> +
> +	if (!rc)
> +		return rc;
> +
> +	s6->sin6_family = AF_INET6;
> +	if (pct) {
> +		s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0);
> +		if (!*pct || *endp)
> +			return 0;
> +	}
> +
> +	return rc;
>  }
>
>  /*****************************************************************************
> --
> 1.6.0.6
>
> _______________________________________________
> linux-cifs-client mailing list
> linux-cifs-client@lists.samba.org
> https://lists.samba.org/mailman/listinfo/linux-cifs-client
>
Jeff Layton June 11, 2009, 4:46 p.m. UTC | #2
On Thu, 11 Jun 2009 16:13:05 +0100 (BST)
"David Holder" <david@erion.co.uk> wrote:

> Jeff,
> 
> Great to see this going into the code. Do you plan to allow for textual
> interface names after the %?
> 
> You can use getnameinfo() with the NI_NUMERICHOST flag to convert the
> textual interface name and IPv6 address to numeric format or just use
> if_nametoindex().
> 
> Regards, David
> 

Yep. Users will be able to use text interface names assuming they have
mount.cifs installed. getaddrinfo will convert textual interface names
to numeric ones already, so we don't really need to do much special for
this. This support will depend on a mount.cifs patch too (sorry, should
have made that clear when I posted the kernel patchset). The mount.cifs
patch is similar, but slightly different from the one posted yesterday.

FWIW, when I was testing today, my mount string was:

mount -t cifs -o sec=none //fe80::21d:7dff:fe9c:3c86%eth0/scratch /mnt/cifs

...and it worked just fine.

I'll send the mount.cifs patch along in a little while...
diff mbox

Patch

diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 6a91b5f..c9e489c 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1374,8 +1374,10 @@  cifs_find_tcp_session(struct sockaddr_storage *addr)
 		     server->addr.sockAddr.sin_addr.s_addr))
 			continue;
 		else if (addr->ss_family == AF_INET6 &&
-			 !ipv6_addr_equal(&server->addr.sockAddr6.sin6_addr,
-					  &addr6->sin6_addr))
+			 (!ipv6_addr_equal(&server->addr.sockAddr6.sin6_addr,
+					   &addr6->sin6_addr) ||
+			  server->addr.sockAddr6.sin6_scope_id !=
+					   addr6->sin6_scope_id))
 			continue;
 
 		++server->srv_count;
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
index 91b5500..8794814 100644
--- a/fs/cifs/dns_resolve.c
+++ b/fs/cifs/dns_resolve.c
@@ -35,7 +35,7 @@ 
  * 		0 - name is not IP
  */
 static int
-is_ip(const char *name)
+is_ip(char *name)
 {
 	struct sockaddr_storage ss;
 
@@ -57,7 +57,7 @@  dns_resolver_instantiate(struct key *key, const void *data,
 	ip[datalen] = '\0';
 
 	/* make sure this looks like an address */
-	if (!is_ip((const char *) ip)) {
+	if (!is_ip(ip)) {
 		kfree(ip);
 		return -EINVAL;
 	}
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index 00e6e35..bd6d689 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -158,25 +158,47 @@  cifs_inet_pton(const int address_family, const char *cp, void *dst)
 /*
  * Try to convert a string to an IPv4 address and then attempt to convert
  * it to an IPv6 address if that fails. Set the family field if either
- * succeeds.
+ * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to
+ * treat the part following it as a numeric sin6_scope_id.
  *
  * Returns 0 on failure.
  */
 int
 cifs_convert_address(char *src, void *dst)
 {
+	int rc;
+	char *pct, *endp;
 	struct sockaddr_in *s4 = (struct sockaddr_in *) dst;
-	struct sockaddr_in6 *s6 = (Struct sockaddr_in6 *) dst;
+	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst;
 
+	/* IPv4 address */
 	if (cifs_inet_pton(AF_INET, src, &s4->sin_addr.s_addr)) {
 		s4->sin_family = AF_INET;
 		return 1;
-	} else if (cifs_inet_pton(AF_INET6, src, &s6->sin6_addr.s6_addr)) {
-		s6->sin6_family = AF_INET6;
-		return 1;
 	}
 
-	return 0;
+	/* temporarily terminate string */
+	pct = strchr(src, '%');
+	if (pct)
+		*pct = '\0';
+
+	rc = cifs_inet_pton(AF_INET6, src, &s6->sin6_addr.s6_addr);
+
+	/* repair temp termination (if any) and make pct point to scopeid */
+	if (pct)
+		*pct++ = '%';
+
+	if (!rc)
+		return rc;
+
+	s6->sin6_family = AF_INET6;
+	if (pct) {
+		s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0);
+		if (!*pct || *endp)
+			return 0;
+	}
+
+	return rc;
 }
 
 /*****************************************************************************