Skip to contentSkip to conversation

NFS: change the ip_map cache code to handle IPv6 addresses

PATCH In review Aurélien Charbon 3 participants 6 messages Aug 9, 2007 – Aug 10, 2007

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 NFS: change the ip_map cache code to handle IPv6 addresses

Here is a small part of missing pieces of IPv6 support for the server. It deals with the ip_map caching code part.

It changes the ip_map structure to be able to store INET6 addresses.
It adds also the changes in address hashing, and mapping to test it with INET addresses.

---

fs/nfsd/export.c               |   12 ++++-
fs/nfsd/nfsctl.c               |   21 ++++++++-
include/linux/sunrpc/svcauth.h |    4 -
net/sunrpc/svcauth_unix.c      |   90 ++++++++++++++++++++++++++---------------
4 files changed, 87 insertions(+), 40 deletions(-)
fs/nfsd/export.c 2007-08-08 17:49:09.000000000 +0200 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +0 -0
diff -u -r -N linux-2.6.23-rc1/fs/nfsd/export.c linux-2.6.23-rc1-IPv6-ip_map/fs/nfsd/export.c
@@ -1558,6 +1558,7 @@
{
    struct auth_domain    *dom;
    int            i, err;

+ struct in6_addr addr6;

    /* First, consistency check. */
    err = -EINVAL;
@@ -1576,9 +1577,14 @@
        goto out_unlock;

    /* Insert client into hashtable. */

- for (i = 0; i < ncp->cl_naddr; i++)
- auth_unix_add_addr(ncp->cl_addrlist[i], dom);
-
+ for (i = 0; i < ncp->cl_naddr; i++) {
+ /* Mapping address */

+            addr6.s6_addr32[0] = 0;
+            addr6.s6_addr32[1] = 0;
+            addr6.s6_addr32[2] = htonl(0xffff);
+            addr6.s6_addr32[3] = (uint32_t)ncp->cl_addrlist[i].s_addr;
+        auth_unix_add_addr(addr6, dom);
+    }
    auth_unix_forget_old(dom);
    auth_domain_put(dom);
fs/nfsd/nfsctl.c 2007-08-08 17:49:09.000000000 +0200 renamed from fs/nfsd/nfsctl.c 2007-08-08 17:52:58.000000000 +0200 +8 -2
diff -u -r -N linux-2.6.23-rc1/fs/nfsd/nfsctl.c linux-2.6.23-rc1-IPv6-ip_map/fs/nfsd/nfsctl.c
@@ -222,7 +222,7 @@
    struct auth_domain *clp;
    int err = 0;
    struct knfsd_fh *res;
-
+    struct in6_addr in6;
    if (size < sizeof(*data))
        return -EINVAL;
    data = (struct nfsctl_fsparm*)buf;
@@ -236,7 +236,14 @@
    res = (struct knfsd_fh*)buf;
 
    exp_readlock();
-    if (!(clp = auth_unix_lookup(sin->sin_addr)))
+   +    /* IPv6 address mapping */
+           in6.s6_addr32[0] = 0;
+           in6.s6_addr32[1] = 0;
+           in6.s6_addr32[2] = htonl(0xffff);
+           in6.s6_addr32[3] = (uint32_t)sin->sin_addr.s_addr;
+
+    if (!(clp = auth_unix_lookup(in6)))
        err = -EPERM;
    else {
        err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
@@ -253,6 +260,7 @@
{
    struct nfsctl_fdparm *data;
    struct sockaddr_in *sin;
+    struct in6_addr in6;
    struct auth_domain *clp;
    int err = 0;
    struct knfsd_fh fh;
@@ -271,7 +279,14 @@
    res = buf;
    sin = (struct sockaddr_in *)&data->gd_addr;
    exp_readlock();
-    if (!(clp = auth_unix_lookup(sin->sin_addr)))
+   +    /* IPv6 address mapping */
+        in6.s6_addr32[0] = 0;
+        in6.s6_addr32[1] = 0;
+        in6.s6_addr32[2] = htonl(0xffff);
+        in6.s6_addr32[3] = (uint32_t)sin->sin_addr.s_addr;
+
+    if (!(clp = auth_unix_lookup(in6)))
        err = -EPERM;
    else {
        err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
include/linux/sunrpc/svcauth.h 2007-08-08 17:48:54.000000000 +0200 renamed from include/linux/sunrpc/svcauth.h 2007-08-08 17:52:59.000000000 +0200 +0 -0
diff -u -r -N linux-2.6.23-rc1/include/linux/sunrpc/svcauth.h linux-2.6.23-rc1-IPv6-ip_map/include/linux/sunrpc/svcauth.h
@@ -120,10 +120,10 @@
 

extern struct auth_domain *unix_domain_find(char *name);
extern void auth_domain_put(struct auth_domain *item);
-extern int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom); +extern int auth_unix_add_addr(struct in6_addr addr, struct auth_domain *dom); extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *new); extern struct auth_domain *auth_domain_find(char *name);
-extern struct auth_domain *auth_unix_lookup(struct in_addr addr); +extern struct auth_domain *auth_unix_lookup(struct in6_addr addr); extern int auth_unix_forget_old(struct auth_domain *dom);
extern void svcauth_unix_purge(void);
extern void svcauth_unix_info_release(void *);

net/sunrpc/svcauth_unix.c 2007-08-09 08:29:27.000000000 +0200 renamed from net/sunrpc/svcauth_unix.c 2007-08-08 17:53:01.000000000 +0200 +0 -0
diff -u -r -N linux-2.6.23-rc1/net/sunrpc/svcauth_unix.c linux-2.6.23-rc1-IPv6-ip_map/net/sunrpc/svcauth_unix.c
@@ -84,7 +84,7 @@
struct ip_map {
    struct cache_head    h;
    char            m_class[8]; /* e.g. "nfsd" */
-    struct in_addr        m_addr;
+    struct in6_addr        m_addr;
    struct unix_domain    *m_client;
    int            m_add_change;
};
@@ -112,12 +112,16 @@
    return (hash ^ (hash>>8)) & 0xff;
}

#endif
+static inline int hash_ip6(struct in6_addr ip)
+{
+ return (hash_ip(ip.s6_addr32[0]) ^ hash_ip(ip.s6_addr32[1]) ^ hash_ip(ip.s6_addr32[2]) ^ hash_ip(ip.s6_addr32[3])) ;
+}
static int ip_map_match(struct cache_head *corig, struct cache_head *cnew)

{
    struct ip_map *orig = container_of(corig, struct ip_map, h);
    struct ip_map *new = container_of(cnew, struct ip_map, h);
    return strcmp(orig->m_class, new->m_class) == 0

- && orig->m_addr.s_addr == new->m_addr.s_addr;
+ && memcmp(orig->m_addr.s6_addr, new->m_addr.s6_addr,sizeof(struct in6_addr));
}
static void ip_map_init(struct cache_head *cnew, struct cache_head *citem) {
@@ -125,7 +129,7 @@

    struct ip_map *item = container_of(citem, struct ip_map, h);

    strcpy(new->m_class, item->m_class);

- new->m_addr.s_addr = item->m_addr.s_addr;
+ memcpy(&new->m_addr.s6_addr, &item->m_addr.s6_addr,sizeof(item->m_addr.s6_addr));
}
static void update(struct cache_head *cnew, struct cache_head *citem) {
@@ -151,20 +155,28 @@

{
    char text_addr[20];
    struct ip_map *im = container_of(h, struct ip_map, h);

- __be32 addr = im->m_addr.s_addr;
+ + __be32 addr[4];
+ int i;
+ for (i=0;i<4;i++)
+ addr[i] = im->m_addr.s6_addr[i];

- snprintf(text_addr, 20, "%u.%u.%u.%u",

-         ntohl(addr) >> 24 & 0xff,
-         ntohl(addr) >> 16 & 0xff,
-         ntohl(addr) >>  8 & 0xff,
-         ntohl(addr) >>  0 & 0xff);
+    snprintf(text_addr, 20, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+            ntohl(addr[3]) >> 16 & 0xff,
+            ntohl(addr[3]) >>  0 & 0xff,
+            ntohl(addr[2]) >> 16 & 0xff,
+            ntohl(addr[2]) >>  0 & 0xff,
+            ntohl(addr[1]) >> 16 & 0xff,
+            ntohl(addr[1]) >>  0 & 0xff,
+            ntohl(addr[0]) >> 16 & 0xff,
+            ntohl(addr[0]) >>  0 & 0xff);
    qword_add(bpp, blen, im->m_class);
    qword_add(bpp, blen, text_addr);
    (*bpp)[-1] = '\n';
}

-static struct ip_map *ip_map_lookup(char *class, struct in_addr addr); +static struct ip_map *ip_map_lookup(char *class, struct in6_addr addr); static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry);

static int ip_map_parse(struct cache_detail *cd,
@@ -175,10 +187,10 @@

     * for scratch: */
    char *buf = mesg;
    int len;
-    int b1,b2,b3,b4;
+    int b1,b2,b3,b4,b5,b6,b7,b8;
    char c;
    char class[8];
-    struct in_addr addr;
+    struct in6_addr addr;
    int err;

    struct ip_map *ipmp;
@@ -197,8 +209,21 @@
    len = qword_get(&mesg, buf, mlen);
    if (len <= 0) return -EINVAL;

- if (sscanf(buf, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
- return -EINVAL;
+ if (sscanf(buf, "%d.%d.%d.%d%c", &b1, &b2, &b3, &b4, &c) == 4) {

+               addr.s6_addr32[0] = 0;
+               addr.s6_addr32[1] = 0;
+               addr.s6_addr32[2] = htonl(0xffff);
+               addr.s6_addr32[3] =
+               htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4);
+       } else if (sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x%c",
+                         &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &c) == 8) {
+               addr.s6_addr32[0] = htonl((b1<<16)|b2);
+               addr.s6_addr32[1] = htonl((b3<<16)|b4);
+               addr.s6_addr32[2] = htonl((b5<<16)|b6);
+               addr.s6_addr32[3] = htonl((b7<<16)|b8);

+ } else
+ return -EINVAL;
+

    expiry = get_expiry(&mesg);
    if (expiry ==0)
@@ -215,9 +240,6 @@
    } else
        dom = NULL;

- addr.s_addr =
- htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4);
-

    ipmp = ip_map_lookup(class,addr);
    if (ipmp) {
        err = ip_map_update(ipmp,
@@ -238,7 +260,7 @@
               struct cache_head *h)
{
    struct ip_map *im;
-    struct in_addr addr;
+    struct in6_addr addr;
    char *dom = "-no-domain-";

    if (h == NULL) {
@@ -247,18 +269,22 @@
    }
    im = container_of(h, struct ip_map, h);
    /* class addr domain */

- addr = im->m_addr;
+ memcpy(&addr, &im->m_addr, sizeof(struct in6_addr));

    if (test_bit(CACHE_VALID, &h->flags) &&
        !test_bit(CACHE_NEGATIVE, &h->flags))
        dom = im->m_client->h.name;

- seq_printf(m, "%s %d.%d.%d.%d %s\n",
+ seq_printf(m, "%s %04x.%04x.%04x.%04x.%04x.%04x.%04x.%04x %s\n",

           im->m_class,
-           ntohl(addr.s_addr) >> 24 & 0xff,
-           ntohl(addr.s_addr) >> 16 & 0xff,
-           ntohl(addr.s_addr) >>  8 & 0xff,
-           ntohl(addr.s_addr) >>  0 & 0xff,
+           ntohl(addr.s6_addr32[3]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[3]) & 0xffff,
+           ntohl(addr.s6_addr32[2]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[2]) & 0xffff,
+           ntohl(addr.s6_addr32[1]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[1]) & 0xffff,
+           ntohl(addr.s6_addr32[0]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[0]) & 0xffff,
           dom
           );
    return 0;

@@ -280,16 +306,16 @@
.alloc = ip_map_alloc,
};

-static struct ip_map *ip_map_lookup(char *class, struct in_addr addr) +static struct ip_map *ip_map_lookup(char *class, struct in6_addr addr)

{
    struct ip_map ip;
    struct cache_head *ch;

    strcpy(ip.m_class, class);

- ip.m_addr = addr;
+ memcpy(&ip.m_addr, &addr, sizeof(struct in6_addr));
ch = sunrpc_cache_lookup(&ip_map_cache, &ip.h,
hash_str(class, IP_HASHBITS) ^
- hash_ip(addr.s_addr));
+ hash_ip6(addr));

    if (ch)
        return container_of(ch, struct ip_map, h);
@@ -318,14 +344,14 @@
    ch = sunrpc_cache_update(&ip_map_cache,
                 &ip.h, &ipm->h,
                 hash_str(ipm->m_class, IP_HASHBITS) ^
-                 hash_ip(ipm->m_addr.s_addr));
+                 hash_ip6(ipm->m_addr));
    if (!ch)
        return -ENOMEM;
    cache_put(ch, &ip_map_cache);
    return 0;
}

-int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom) +int auth_unix_add_addr(struct in6_addr addr, struct auth_domain *dom)

{
    struct unix_domain *udom;
    struct ip_map *ipmp;
@@ -352,7 +378,7 @@
    return 0;
}

-struct auth_domain *auth_unix_lookup(struct in_addr addr) +struct auth_domain *auth_unix_lookup(struct in6_addr addr)

{
    struct ip_map *ipm;
    struct auth_domain *rv;

@@ -641,7 +667,7 @@
int
svcauth_unix_set_client(struct svc_rqst *rqstp)
{
- struct sockaddr_in *sin = svc_addr_in(rqstp);

+    struct sockaddr_in6 *sin = svc_addr_in6(rqstp);
    struct ip_map *ipm;

    rqstp->rq_client = NULL;
@@ -651,7 +677,7 @@
    ipm = ip_map_cached_get(rqstp);
    if (ipm == NULL)
        ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class,

- sin->sin_addr);
+ sin->sin6_addr);

    if (ipm == NULL)
        return SVC_DENIED;
Signature · 7 lines
********************************
      Aurelien Charbon
      Linux NFSv4 team
          Bull SAS
    Echirolles - France
http://nfsv4.bullopensource.org/
********************************
#1 lore raw
Quoted 5 lines · Aurélien Charbon

Here is a small part of missing pieces of IPv6 support for the server. It deals with the ip_map caching code part.

It changes the ip_map structure to be able to store INET6 addresses.
It adds also the changes in address hashing, and mapping to test it with INET addresses.

Thanks for posting your patch.

Your strategy is to convert all incoming IPv4 addresses in the ip_map cache to IPv6 addresses, and use only IPv6 internally (often suggested by IPv6 books I've encountered). For NFS, that is problematic because these addresses are used as the target of access control rules for exports; thus sys admins will expect to see IPv4 addresses in the output of NFS utilities if they specified IPv4 addresses in their /etc/exports file, for example.

Some naive questions:

1. If IPv6 support is not configured into the kernel, how does an IPv6-only cache work?

2. I seem to recall (only quite vaguely) that at some point the server might need to use one of the stored addresses to, say, open a socket to the client? In that case, on a system with NICs configured only with IPv4, is the cached IPv6 address properly converted back to IPv4 somehow? Can the cache code tell the difference between a cached IPv6 address that was converted from IPv4 and one that was added to the cache as IPv6? Sorry I can't remember more clearly.

3. Would it be better to make the m_addr field a struct sockaddr, store a whole address (with address family), and switch on the sa_family field?

Quoted 19 lines

diff -u -r -N linux-2.6.23-rc1/fs/nfsd/export.c linux-2.6.23-rc1-IPv6-ip_map/fs/nfsd/export.c

fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +0 -0

17:49:09.000000000 +0200

fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +1 -3
@@ -1558,6 +1558,7 @@
 {
     struct auth_domain    *dom;
     int            i, err;
+    struct in6_addr addr6;
 
     /* First, consistency check. */
     err = -EINVAL;
@@ -1576,9 +1577,14 @@
         goto out_unlock;
 
     /* Insert client into hashtable. */
-    for (i = 0; i < ncp->cl_naddr; i++)
-        auth_unix_add_addr(ncp->cl_addrlist[i], dom);
-

fs/nfsd/export.c 2007-08-08 line 1589

Quoted 10 lines
fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +8 -0
+    for (i = 0; i < ncp->cl_naddr; i++) {
+        /* Mapping address */
+            addr6.s6_addr32[0] = 0;
+            addr6.s6_addr32[1] = 0;
+            addr6.s6_addr32[2] = htonl(0xffff);
+            addr6.s6_addr32[3] = (uint32_t)ncp->cl_addrlist[i].s_addr;
+        auth_unix_add_addr(addr6, dom);
+    }
     auth_unix_forget_old(dom);
     auth_domain_put(dom);

This converts IPv4 addresses to canonical IPv6 as it stores them. What happens if a full-blown IPv6 address is encountered? Likewise, in nfsctl.c?

fs/nfsd/export.c 2007-08-08 line 119

Quoted 9 lines
fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +4 -0
@@ -112,12 +112,16 @@
     return (hash ^ (hash>>8)) & 0xff;
 }
 #endif
+static inline int hash_ip6(struct in6_addr ip)
+{
+        return (hash_ip(ip.s6_addr32[0]) ^ hash_ip(ip.s6_addr32[1]) ^ 
 hash_ip(ip.s6_addr32[2]) ^ hash_ip(ip.s6_addr32[3])) ;
+}

How have you tested the effectiveness of the new hash function?

Quoted 15 lines
fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +5 -5
@@ -151,20 +155,28 @@
 {
     char text_addr[20];
     struct ip_map *im = container_of(h, struct ip_map, h);
-    __be32 addr = im->m_addr.s_addr;
+   
+    __be32 addr[4];
+    int i;
+    for (i=0;i<4;i++)
+        addr[i] = im->m_addr.s6_addr[i];
 
-    snprintf(text_addr, 20, "%u.%u.%u.%u",
-         ntohl(addr) >> 24 & 0xff,
-         ntohl(addr) >> 16 & 0xff,
-         ntohl(addr) >>  8 & 0xff,

fs/nfsd/export.c 2007-08-08 line 172

Quoted 10 lines
fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +9 -1
-         ntohl(addr) >>  0 & 0xff);
+    snprintf(text_addr, 20, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+            ntohl(addr[3]) >> 16 & 0xff,
+            ntohl(addr[3]) >>  0 & 0xff,
+            ntohl(addr[2]) >> 16 & 0xff,
+            ntohl(addr[2]) >>  0 & 0xff,
+            ntohl(addr[1]) >> 16 & 0xff,
+            ntohl(addr[1]) >>  0 & 0xff,
+            ntohl(addr[0]) >> 16 & 0xff,
+            ntohl(addr[0]) >>  0 & 0xff);

The snprintf() format strings should use NIP6_FMT.

Quoted 12 lines
fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +6 -2
@@ -197,8 +209,21 @@
     len = qword_get(&mesg, buf, mlen);
     if (len <= 0) return -EINVAL;
 
-    if (sscanf(buf, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
-        return -EINVAL;
+    if (sscanf(buf, "%d.%d.%d.%d%c", &b1, &b2, &b3, &b4, &c) == 4) {
+               addr.s6_addr32[0] = 0;
+               addr.s6_addr32[1] = 0;
+               addr.s6_addr32[2] = htonl(0xffff);
+               addr.s6_addr32[3] =
+               htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4);

fs/nfsd/export.c 2007-08-08 line 227

Quoted 10 lines
fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +9 -0
+       } else if (sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x%c",
+                         &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &c) == 
 8) {
+               addr.s6_addr32[0] = htonl((b1<<16)|b2);
+               addr.s6_addr32[1] = htonl((b3<<16)|b4);
+               addr.s6_addr32[2] = htonl((b5<<16)|b6);
+               addr.s6_addr32[3] = htonl((b7<<16)|b8);
+       } else
+                return -EINVAL;
+

Likewise, the sscanf() format strings should use NIP6_FMT.

Quoted 19 lines
fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +3 -6
@@ -247,18 +269,22 @@
     }
     im = container_of(h, struct ip_map, h);
     /* class addr domain */
-    addr = im->m_addr;
+    memcpy(&addr, &im->m_addr, sizeof(struct in6_addr));
 
     if (test_bit(CACHE_VALID, &h->flags) &&
         !test_bit(CACHE_NEGATIVE, &h->flags))
         dom = im->m_client->h.name;
 
-    seq_printf(m, "%s %d.%d.%d.%d %s\n",
+    seq_printf(m, "%s %04x.%04x.%04x.%04x.%04x.%04x.%04x.%04x %s\n",
            im->m_class,
-           ntohl(addr.s_addr) >> 24 & 0xff,
-           ntohl(addr.s_addr) >> 16 & 0xff,
-           ntohl(addr.s_addr) >>  8 & 0xff,
-           ntohl(addr.s_addr) >>  0 & 0xff,
+           ntohl(addr.s6_addr32[3]) >> 16 & 0xffff,

fs/nfsd/export.c 2007-08-08 line 290

Quoted 10 lines
fs/nfsd/export.c 2007-08-08 renamed from fs/nfsd/export.c 2007-08-08 17:52:58.000000000 +0200 +7 -0
+           ntohl(addr.s6_addr32[3]) & 0xffff,
+           ntohl(addr.s6_addr32[2]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[2]) & 0xffff,
+           ntohl(addr.s6_addr32[1]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[1]) & 0xffff,
+           ntohl(addr.s6_addr32[0]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[0]) & 0xffff,
            dom
            );
     return 0;

And I think here NIP6_FMT should be used, but you're not using colons between the hex digits. Was that intentional?

begin:vcard
fn:Chuck Lever
n:Lever;Chuck
org:Oracle Corporation;Corporate Architecture: Linux Projects Group adr:;;1015 Granger Avenue;Ann Arbor;MI;48104;USA title:Principal Member of Staff
tel;work:+1 248 614 5091
x-mozilla-html:FALSE
url:http://oss.oracle.com/~cel
version:2.1
end:vcard

#2 lore raw

Thanks for these comments.

Quoted 3 lines · Chuck Lever

Some naive questions:

1. If IPv6 support is not configured into the kernel, how does an IPv6-only cache work?

Yes it should work.
The INET6 structures are only used as containers for both type of address. I have tested by unabling IPv6 options in the .config file.

Quoted 2 lines

2. I seem to recall (only quite vaguely) that at some point the server might need to use one of the stored addresses to, say, open a socket to the client? In that case, on a system with NICs configured only with IPv4, is the cached IPv6 address properly converted back to IPv4 somehow?

Yes. we can do that by testing the format of the address, as we know that an IPv4 address is mapped like that:
[00000000] [00000000] [0000FFFF] <32 bits of IPv4 addr>

Quoted 1 line

Can the cache code tell the difference between a cached IPv6 address that was converted from IPv4 and one that was added to the cache as IPv6? Sorry I can't remember more clearly.

Yes. If the address starts with ::FFFF, we can infer that is a mapped IPv4 address.

Quoted 2 lines

3. Would it be better to make the m_addr field a struct sockaddr, store a whole address (with address family), and switch on the sa_family field?

That is possible but we're able to retrieve the family of the address by testing its format (IPv4 mapped or not). But I don't know if it is a better solution.

Quoted 27 lines
Quoted 27 lines

diff -u -r -N linux-2.6.23-rc1/fs/nfsd/export.c linux-2.6.23-rc1-IPv6-ip_map/fs/nfsd/export.c
--- linux-2.6.23-rc1/fs/nfsd/export.c 2007-08-08
17:52:58.000000000 +0200
+++ linux-2.6.23-rc1-IPv6-ip_map/fs/nfsd/export.c 2007-08-08
17:49:09.000000000 +0200

(quoted diff) +6 -3
@@ -1558,6 +1558,7 @@
 {
     struct auth_domain    *dom;
     int            i, err;
+    struct in6_addr addr6;
 
     /* First, consistency check. */
     err = -EINVAL;
@@ -1576,9 +1577,14 @@
         goto out_unlock;
 
     /* Insert client into hashtable. */
-    for (i = 0; i < ncp->cl_naddr; i++)
-        auth_unix_add_addr(ncp->cl_addrlist[i], dom);
-
+    for (i = 0; i < ncp->cl_naddr; i++) {
+        /* Mapping address */
+            addr6.s6_addr32[0] = 0;
+            addr6.s6_addr32[1] = 0;
+            addr6.s6_addr32[2] = htonl(0xffff);
Quoted 8 lines

Review line 1589

Quoted 5 lines
(quoted diff) +3 -0
+            addr6.s6_addr32[3] = (uint32_t)ncp->cl_addrlist[i].s_addr;
+        auth_unix_add_addr(addr6, dom);
+    }
     auth_unix_forget_old(dom);
     auth_domain_put(dom);

This converts IPv4 addresses to canonical IPv6 as it stores them. What happens if a full-blown IPv6 address is encountered? Likewise, in nfsctl.c?

I have done this kind of convertion only for compilation an testing with only IPv4... For the moment.

Quoted 13 lines

Review line 119

Quoted 10 lines
(quoted diff) +4 -0
@@ -112,12 +112,16 @@
     return (hash ^ (hash>>8)) & 0xff;
 }
 #endif
+static inline int hash_ip6(struct in6_addr ip)
+{
+        return (hash_ip(ip.s6_addr32[0]) ^ hash_ip(ip.s6_addr32[1]) 
 ^ hash_ip(ip.s6_addr32[2]) ^ hash_ip(ip.s6_addr32[3])) ;
+}

How have you tested the effectiveness of the new hash function?

I have not tested that point but I can easily imagine there are better solutions.
Perhaps we can keep the same function for an IPv4 address (only taking the 32 bits of IPv4 addr), and then design one for IPv6 addresses.

Do you have any suggestion on that ?

Quoted 20 lines
Quoted 8 lines
(quoted diff) +2 -5
-    seq_printf(m, "%s %d.%d.%d.%d %s\n",
+    seq_printf(m, "%s %04x.%04x.%04x.%04x.%04x.%04x.%04x.%04x %s\n",
            im->m_class,
-           ntohl(addr.s_addr) >> 24 & 0xff,
-           ntohl(addr.s_addr) >> 16 & 0xff,
-           ntohl(addr.s_addr) >>  8 & 0xff,
-           ntohl(addr.s_addr) >>  0 & 0xff,
+           ntohl(addr.s6_addr32[3]) >> 16 & 0xffff,

Review

Quoted 10 lines
(quoted diff) +7 -0
+           ntohl(addr.s6_addr32[3]) & 0xffff,
+           ntohl(addr.s6_addr32[2]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[2]) & 0xffff,
+           ntohl(addr.s6_addr32[1]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[1]) & 0xffff,
+           ntohl(addr.s6_addr32[0]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[0]) & 0xffff,
            dom
            );
     return 0;
Quoted 2 lines

And I think here NIP6_FMT should be used, but you're not using colons between the hex digits. Was that intentional ?

No that's a mistake. Colons have to be used here.

Aurélien

Signature · 7 lines
********************************
      Aurelien Charbon
      Linux NFSv4 team
          Bull SAS
    Echirolles - France
http://nfsv4.bullopensource.org/
********************************
#3 lore raw
Quoted 13 lines · Aurélien Charbon
Quoted 4 lines
Quoted 4 lines
(quoted diff) +0 -0
@@ -112,12 +112,16 @@
     return (hash ^ (hash>>8)) & 0xff;
 }
 #endif
Quoted 8 lines

Review line 119

Quoted 5 lines
(quoted diff) +4 -0
+static inline int hash_ip6(struct in6_addr ip)
+{
+        return (hash_ip(ip.s6_addr32[0]) ^ hash_ip(ip.s6_addr32[1]) 
 ^ hash_ip(ip.s6_addr32[2]) ^ hash_ip(ip.s6_addr32[3])) ;
+}

How have you tested the effectiveness of the new hash function?

Quoted 2 lines

I have not tested that point but I can easily imagine there are better solutions.
Perhaps we can keep the same function for an IPv4 address (only taking the 32 bits of IPv4 addr), and then design one for IPv6 addresses.

I see that, to generate the hash, you would be xor-ing the FF and 00 bytes in the canonicalized IPv4 address. Yes, perhaps a better function is needed, or as you say, one specifically for IPv6 and one for canonicalized IPv4.

Quoted 1 line

Do you have any suggestion on that ?

I don't have anything specific, but you may find something useful if you poke around elsewhere under net/.

begin:vcard
fn:Chuck Lever
n:Lever;Chuck
org:Oracle Corporation;Corporate Architecture: Linux Projects Group adr:;;1015 Granger Avenue;Ann Arbor;MI;48104;USA title:Principal Member of Staff
tel;work:+1 248 614 5091
x-mozilla-html:FALSE
url:http://oss.oracle.com/~cel
version:2.1
end:vcard

#4 lore raw
Quoted 8 lines · On Thursday August 9, [email protected] wrote:
Quoted 7 lines
Quoted 2 lines

How have you tested the effectiveness of the new hash function?

I have not tested that point but I can easily imagine there are better solutions.
Perhaps we can keep the same function for an IPv4 address (only taking the 32 bits of IPv4 addr), and then design one for IPv6 addresses.

Quoted 5 lines

I see that, to generate the hash, you would be xor-ing the FF and 00 bytes in the canonicalized IPv4 address. Yes, perhaps a better function is needed, or as you say, one specifically for IPv6 and one for canonicalized IPv4.

I suspect that the given hash function will be as good as any other. The values in each byte of an IPv6 are likely to be either evenly distributed, or constant.
Each the first case you don't need a clever hash function to distribute them, while in the second, no hash function can improve the distribution.

NeilBrown

#5 lore raw
In reply to Aurélien Charbon
Quoted 6 lines · On Thursday August 9, [email protected] wrote:

Here is a small part of missing pieces of IPv6 support for the server. It deals with the ip_map caching code part.

It changes the ip_map structure to be able to store INET6 addresses. It adds also the changes in address hashing, and mapping to test it with INET addresses.

Thanks - this generally seems to make sense. A few specific comments:

Review

Quoted 1 line
(quoted diff) +0 -0
@@ -1558,6 +1558,7 @@

Please use "diff -p" to include the C function name. It makes the patch easier to read.

Review

Quoted 8 lines
(quoted diff) +8 -0
+    for (i = 0; i < ncp->cl_naddr; i++) {
+        /* Mapping address */
+            addr6.s6_addr32[0] = 0;
+            addr6.s6_addr32[1] = 0;
+            addr6.s6_addr32[2] = htonl(0xffff);
+            addr6.s6_addr32[3] = (uint32_t)ncp->cl_addrlist[i].s_addr;
+        auth_unix_add_addr(addr6, dom);
+    }

You do this conversion several times, and each time it is indented strangely... Maybe create an inline (if there isn't one already) to do this.

Quoted 5 lines

extern void auth_domain_put(struct auth_domain *item);
-extern int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom);
+extern int auth_unix_add_addr(struct in6_addr addr, struct auth_domain *dom);

Your mailer is wrapping long lines. This is bad.

Quoted 9 lines

static int ip_map_match(struct cache_head *corig, struct cache_head *cnew)

 {
     struct ip_map *orig = container_of(corig, struct ip_map, h);
     struct ip_map *new = container_of(cnew, struct ip_map, h);
     return strcmp(orig->m_class, new->m_class) == 0
(quoted diff) +1 -1
-        && orig->m_addr.s_addr == new->m_addr.s_addr;
+        && memcmp(orig->m_addr.s6_addr, 

new->m_addr.s6_addr,sizeof(struct in6_addr));
}

I think you have inverted the sense of the test. The original tests for equality. The new tests for inequality. For memcmp (and strcmp),
always use "function(arg1, argc) COMPARE_OP 0"
e.g.

            memcmp(orig, new, size) == 0

or use ipv6_addr_equal.

Also please run ./scripts/checkpatch.pl on the patch. You need a space after that comma.

Review line 133

Quoted 7 lines
(quoted diff) +1 -1
@@ -125,7 +129,7 @@
     struct ip_map *item = container_of(citem, struct ip_map, h);
 
     strcpy(new->m_class, item->m_class);
-    new->m_addr.s_addr = item->m_addr.s_addr;
+    memcpy(&new->m_addr.s6_addr, 
 &item->m_addr.s6_addr,sizeof(item->m_addr.s6_addr));

Ditto..... did you test this code?

Review line 162

Quoted 13 lines
(quoted diff) +0 -0
 }
 static void update(struct cache_head *cnew, struct cache_head *citem)

{

(quoted diff) +5 -1
@@ -151,20 +155,28 @@
 {
     char text_addr[20];
     struct ip_map *im = container_of(h, struct ip_map, h);
-    __be32 addr = im->m_addr.s_addr;
+   
+    __be32 addr[4];
+    int i;
+    for (i=0;i<4;i++)
+        addr[i] = im->m_addr.s6_addr[i];
                                   ^^^^

I think you mean "s6_addr32" ??

Review line 164

Quoted 8 lines
(quoted diff) +2 -5
-    snprintf(text_addr, 20, "%u.%u.%u.%u",
-         ntohl(addr) >> 24 & 0xff,
-         ntohl(addr) >> 16 & 0xff,
-         ntohl(addr) >>  8 & 0xff,
-         ntohl(addr) >>  0 & 0xff);
+    snprintf(text_addr, 20, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+            ntohl(addr[3]) >> 16 & 0xff,
                                      ^^^^

should be 0xffff ???

Now I know you didn't test the code :-)

Review line 171

Quoted 7 lines
(quoted diff) +7 -0
+            ntohl(addr[3]) >>  0 & 0xff,
+            ntohl(addr[2]) >> 16 & 0xff,
+            ntohl(addr[2]) >>  0 & 0xff,
+            ntohl(addr[1]) >> 16 & 0xff,
+            ntohl(addr[1]) >>  0 & 0xff,
+            ntohl(addr[0]) >> 16 & 0xff,
+            ntohl(addr[0]) >>  0 & 0xff);

This code would read better if you did
__be16 addr[8];
for (i = 0; i < 8 ; i++)

	addr[i] = im->m_addr.s6_addr16[i];
   snprintf(........
		ntohs(addr[7]),
		ntohs(addr[6]),
		....

Also, I think that if it is a (converted) IPv4 address, it should be printed as a dotted-quad.

Review line 175

Quoted 4 lines
(quoted diff) +4 -0
+               addr.s6_addr32[0] = htonl((b1<<16)|b2);
+               addr.s6_addr32[1] = htonl((b3<<16)|b4);
+               addr.s6_addr32[2] = htonl((b5<<16)|b6);
+               addr.s6_addr32[3] = htonl((b7<<16)|b8);

Assign to addr.s6_addr16[].

Quoted 5 lines
(quoted diff) +1 -3
-    seq_printf(m, "%s %d.%d.%d.%d %s\n",
+    seq_printf(m, "%s %04x.%04x.%04x.%04x.%04x.%04x.%04x.%04x %s\n",
            im->m_class,
-           ntohl(addr.s_addr) >> 24 & 0xff,
-           ntohl(addr.s_addr) >> 16 & 0xff,

Review

Quoted 10 lines
(quoted diff) +8 -2
-           ntohl(addr.s_addr) >>  8 & 0xff,
-           ntohl(addr.s_addr) >>  0 & 0xff,
+           ntohl(addr.s6_addr32[3]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[3]) & 0xffff,
+           ntohl(addr.s6_addr32[2]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[2]) & 0xffff,
+           ntohl(addr.s6_addr32[1]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[1]) & 0xffff,
+           ntohl(addr.s6_addr32[0]) >> 16 & 0xffff,
+           ntohl(addr.s6_addr32[0]) & 0xffff,

Again, s6_addr16[], and keep the dotted-quad for IPv4. In fact ( borrowing Chuck's suggestion)
if (is_ipv4)

	seq_printf(m, "%s "NIPQUAD_FMT" %s\n", im->m_class,
			NIPQUAD((addr.s6_addr32[0])), ....);
 else
	seq_printf(m, "%s "NIP6_FMT" %s\n", im->m_class,
			NIP6(addr), ...);

Thanks,
NeilBrown