[RFC] CIFS: Make sure IPv6 host uses numeric scope

"\"Per Forlin <per.forlin" at axis.com "\"Per Forlin <per.forlin" at axis.com
Mon Sep 19 12:59:29 UTC 2016


From: Per Förlin <perfn at axis.com>

Package: busybox
Version: master (1_25_0+)
Severity: medium

The purpose of this draft patch is to highlight an
issue concerning address scope in IPv6 and CIFS kernel implementation.

Without this patch the following command fails:
mount -t cifs //fe80::6a05:caff:fe3e:dbf5%eth0/test test

Based on this post
http://patchwork.sourceware.org/patch/1629/
it seems like this topic has been up for discussion before.
However it looks like the NI_NUMERICSCOPE flag for getnaminfo()
never made it to glibc.

getnameinfo() always returns the address scope using the
name representation but the CIFS kernel implementation
expects a numeric scope ID.

There are different ways to approach this.
1. Parse the host string manually and converting the scope
   using if_nametoindex().
2. Convert the host address to binary and back again.

This patch uses alternative #2.
Re-create the host address using the numeric scope ID retrieved
by getaddrinfo().
---
 include/libbb.h    |  1 +
 libbb/xconnect.c   | 57 ++++++++++++++++++++++++++++++++++++++++++++++++------
 util-linux/mount.c |  2 +-
 3 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/include/libbb.h b/include/libbb.h
index 3752df9..48e110a 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -688,6 +688,7 @@ char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) FAST_FUNC RETU
 /* inet_[ap]ton on steroids */
 char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
 char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
+char* xmalloc_sockaddr2dotted_noport_num(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
 // "old" (ipv4 only) API
 // users: traceroute.c hostname.c - use _list_ of all IPs
 struct hostent *xgethostbyname(const char *name) FAST_FUNC;
diff --git a/libbb/xconnect.c b/libbb/xconnect.c
index 6e78e63..5095c35 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -433,10 +433,45 @@ int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa)
 	return fd;
 }
 
+/* Make sure the host is specified with a numeric scope.
+ * Numeric scope is required by the CIFS kernel implementation. */
+static int host_make_numeric_scope(char * host, int host_size) {
+	struct addrinfo *addr = NULL;
+	struct sockaddr_in6 *sin6;
+	int res = -1;
+
+	if (getaddrinfo(host, NULL, NULL, &addr) != 0)
+		goto out;
+
+	if (addr->ai_family != AF_INET6)
+		goto out;
+
+	sin6 = (struct sockaddr_in6 *)addr->ai_addr;
+	if (!sin6->sin6_scope_id) {
+		res = 0; /* no scope to be processed */
+		goto out;
+	}
+
+	if (inet_ntop(AF_INET6, &sin6->sin6_addr, host, host_size) != NULL) {
+		int len = strnlen(host, host_size);
+		if (snprintf(host + len, host_size - len,
+			     "%%%u", sin6->sin6_scope_id) > 1)
+			res = 0;
+	}
+
+out:
+	if (res != 0)
+		bb_perror_msg("Failed to convert %s to numeric scope", host);
+
+	freeaddrinfo(addr);
+	return res;
+}
+
 /* We hijack this constant to mean something else */
 /* It doesn't hurt because we will add this bit anyway */
 #define IGNORE_PORT NI_NUMERICSERV
-static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags)
+static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags,
+				    int num_scope)
 {
 	char host[128];
 	char serv[16];
@@ -464,6 +499,10 @@ static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags)
 			/* do not resolve port# into service _name_ */
 			flags | NI_NUMERICSERV
 	);
+	if (num_scope && sa->sa_family == AF_INET6 &&
+	    host_make_numeric_scope(host, sizeof(host)) != 0)
+		return NULL;
+
 	if (rc)
 		return NULL;
 	if (flags & IGNORE_PORT)
@@ -484,24 +523,30 @@ static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags)
 
 char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa)
 {
-	return sockaddr2str(sa, 0);
+	return sockaddr2str(sa, 0, false);
 }
 
 char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa)
 {
-	return sockaddr2str(sa, IGNORE_PORT);
+	return sockaddr2str(sa, IGNORE_PORT, false);
 }
 
 char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa)
 {
-	return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT);
+	return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT, false);
 }
 char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa)
 {
-	return sockaddr2str(sa, NI_NUMERICHOST);
+	return sockaddr2str(sa, NI_NUMERICHOST, false);
 }
 
 char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa)
 {
-	return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT);
+	return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT, false);
+}
+
+char* FAST_FUNC xmalloc_sockaddr2dotted_noport_num(const struct sockaddr *sa)
+{
+	return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT,
+			    true /* numeric scope */);
 }
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 13590ce..b87be90 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -1972,7 +1972,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
 			goto report_error;
 
 		// Insert "ip=..." option into options
-		dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+		dotted = xmalloc_sockaddr2dotted_noport_num(&lsa->u.sa);
 		if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
 		ip = xasprintf("ip=%s", dotted);
 		if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
-- 
2.1.4



More information about the busybox mailing list