NLM: Support IPv6 scope IDs in nlm_display_address()
authorChuck Lever <chuck.lever@oracle.com>
Thu, 4 Dec 2008 19:20:08 +0000 (14:20 -0500)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Tue, 6 Jan 2009 16:53:49 +0000 (11:53 -0500)
Scope ID support is needed since the kernel's NSM implementation is
about to use these displayed addresses as a mon_name in some cases.

When nsm_use_hostnames is zero, without scope ID support NSM will fail
to handle peers that contact us via a link-local address.  Link-local
addresses do not work without an interface ID, which is stored in the
sockaddr's sin6_scope_id field.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
fs/lockd/host.c
include/linux/lockd/lockd.h

index beb5da810167610333bee1558fdcb0c7338b18fa..012e49aaecd130ddb7b10f384523290af0bf6a24 100644 (file)
@@ -105,22 +105,31 @@ static void nlm_clear_port(struct sockaddr *sap)
        }
 }
 
+static void nlm_display_ipv6_address(const struct sockaddr *sap, char *buf,
+                                    const size_t len)
+{
+       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+
+       if (ipv6_addr_v4mapped(&sin6->sin6_addr))
+               snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]);
+       else if (sin6->sin6_scope_id != 0)
+               snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr,
+                               sin6->sin6_scope_id);
+       else
+               snprintf(buf, len, "%pI6", &sin6->sin6_addr);
+}
+
 static void nlm_display_address(const struct sockaddr *sap,
                                char *buf, const size_t len)
 {
        const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
-       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
 
        switch (sap->sa_family) {
        case AF_INET:
                snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
                break;
        case AF_INET6:
-               if (ipv6_addr_v4mapped(&sin6->sin6_addr))
-                       snprintf(buf, len, "%pI4",
-                                &sin6->sin6_addr.s6_addr32[3]);
-               else
-                       snprintf(buf, len, "%pI6", &sin6->sin6_addr);
+               nlm_display_ipv6_address(sap, buf, len);
                break;
        default:
                snprintf(buf, len, "unsupported address family");
index dae22cb4c38d020f87ef37e8915461961afb8e07..80a0a2cff2b826fbd4ee0fe0cfdc35c6967af13d 100644 (file)
@@ -68,6 +68,14 @@ struct nlm_host {
        char                    *h_addrbuf;     /* address eyecatcher */
 };
 
+/*
+ * The largest string sm_addrbuf should hold is a full-size IPv6 address
+ * (no "::" anywhere) with a scope ID.  The buffer size is computed to
+ * hold eight groups of colon-separated four-hex-digit numbers, a
+ * percent sign, a scope id (at most 32 bits, in decimal), and NUL.
+ */
+#define NSM_ADDRBUF            ((8 * 4 + 7) + (1 + 10) + 1)
+
 struct nsm_handle {
        struct list_head        sm_link;
        atomic_t                sm_count;
@@ -76,7 +84,7 @@ struct nsm_handle {
        size_t                  sm_addrlen;
        unsigned int            sm_monitored : 1,
                                sm_sticky : 1;  /* don't unmonitor */
-       char                    sm_addrbuf[48]; /* address eyecatcher */
+       char                    sm_addrbuf[NSM_ADDRBUF];
 };
 
 /*