cifs: have cifs parse scope_id out of IPv6 addresses and use it
authorJeff Layton <jlayton@redhat.com>
Thu, 11 Jun 2009 14:27:31 +0000 (10:27 -0400)
committerSteve French <sfrench@us.ibm.com>
Thu, 25 Jun 2009 01:14:36 +0000 (01:14 +0000)
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>
Acked-by: David Holder <david@erion.co.uk>
Signed-off-by: Steve French <sfrench@us.ibm.com>
fs/cifs/connect.c
fs/cifs/dns_resolve.c
fs/cifs/netmisc.c

index c368ad6..3fb799f 100644 (file)
@@ -1386,8 +1386,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;
index 91b5500..8794814 100644 (file)
@@ -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;
        }
index f9a54da..bd6d689 100644 (file)
@@ -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;
 
+       /* 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;
 }
 
 /*****************************************************************************