From 681bf72e4893a187cf6b6b62c08fc193f81c8c2f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 11 Jun 2009 10:27:31 -0400 Subject: [PATCH] cifs: have cifs parse scope_id out of IPv6 addresses and use it 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 Acked-by: David Holder Signed-off-by: Steve French --- fs/cifs/connect.c | 6 ++++-- fs/cifs/dns_resolve.c | 4 ++-- fs/cifs/netmisc.c | 32 +++++++++++++++++++++++++++----- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c368ad65823..3fb799ff55c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -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; diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 91b5500755b..87948147d7e 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 f9a54da97d3..bd6d6895730 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; + /* 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; } /*****************************************************************************