Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

IPv6 support for NFS server export caches

This adds IPv6 support to the interfaces that are used to express nfsd
exports. All addressed are stored internally as IPv6; backwards
compatibility is maintained using mapped addresses.

Thanks to Bruce Fields, Brian Haley, Neil Brown and Hideaki Joshifuji
for comments

Signed-off-by: Aurelien Charbon <aurelien.charbon@bull.net>
Cc: Neil Brown <neilb@suse.de>
Cc: Brian Haley <brian.haley@hp.com>
Cc: YOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>

authored by

Aurélien Charbon and committed by
J. Bruce Fields
f15364bd d751a7cd

+111 -45
+6 -3
fs/nfsd/export.c
··· 35 35 #include <linux/lockd/bind.h> 36 36 #include <linux/sunrpc/msg_prot.h> 37 37 #include <linux/sunrpc/gss_api.h> 38 + #include <net/ipv6.h> 38 39 39 40 #define NFSDDBG_FACILITY NFSDDBG_EXPORT 40 41 ··· 1549 1548 { 1550 1549 struct auth_domain *dom; 1551 1550 int i, err; 1551 + struct in6_addr addr6; 1552 1552 1553 1553 /* First, consistency check. */ 1554 1554 err = -EINVAL; ··· 1568 1566 goto out_unlock; 1569 1567 1570 1568 /* Insert client into hashtable. */ 1571 - for (i = 0; i < ncp->cl_naddr; i++) 1572 - auth_unix_add_addr(ncp->cl_addrlist[i], dom); 1573 - 1569 + for (i = 0; i < ncp->cl_naddr; i++) { 1570 + ipv6_addr_set_v4mapped(ncp->cl_addrlist[i].s_addr, &addr6); 1571 + auth_unix_add_addr(&addr6, dom); 1572 + } 1574 1573 auth_unix_forget_old(dom); 1575 1574 auth_domain_put(dom); 1576 1575
+13 -2
fs/nfsd/nfsctl.c
··· 37 37 #include <linux/nfsd/syscall.h> 38 38 39 39 #include <asm/uaccess.h> 40 + #include <net/ipv6.h> 40 41 41 42 /* 42 43 * We have a single directory with 9 nodes in it. ··· 223 222 struct auth_domain *clp; 224 223 int err = 0; 225 224 struct knfsd_fh *res; 225 + struct in6_addr in6; 226 226 227 227 if (size < sizeof(*data)) 228 228 return -EINVAL; ··· 238 236 res = (struct knfsd_fh*)buf; 239 237 240 238 exp_readlock(); 241 - if (!(clp = auth_unix_lookup(sin->sin_addr))) 239 + 240 + ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6); 241 + 242 + clp = auth_unix_lookup(&in6); 243 + if (!clp) 242 244 err = -EPERM; 243 245 else { 244 246 err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); ··· 263 257 int err = 0; 264 258 struct knfsd_fh fh; 265 259 char *res; 260 + struct in6_addr in6; 266 261 267 262 if (size < sizeof(*data)) 268 263 return -EINVAL; ··· 278 271 res = buf; 279 272 sin = (struct sockaddr_in *)&data->gd_addr; 280 273 exp_readlock(); 281 - if (!(clp = auth_unix_lookup(sin->sin_addr))) 274 + 275 + ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6); 276 + 277 + clp = auth_unix_lookup(&in6); 278 + if (!clp) 282 279 err = -EPERM; 283 280 else { 284 281 err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
+3 -2
include/linux/sunrpc/svcauth.h
··· 24 24 }; 25 25 26 26 struct svc_rqst; /* forward decl */ 27 + struct in6_addr; 27 28 28 29 /* Authentication is done in the context of a domain. 29 30 * ··· 121 120 122 121 extern struct auth_domain *unix_domain_find(char *name); 123 122 extern void auth_domain_put(struct auth_domain *item); 124 - extern int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom); 123 + extern int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom); 125 124 extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *new); 126 125 extern struct auth_domain *auth_domain_find(char *name); 127 - extern struct auth_domain *auth_unix_lookup(struct in_addr addr); 126 + extern struct auth_domain *auth_unix_lookup(struct in6_addr *addr); 128 127 extern int auth_unix_forget_old(struct auth_domain *dom); 129 128 extern void svcauth_unix_purge(void); 130 129 extern void svcauth_unix_info_release(void *);
+9
include/net/ipv6.h
··· 383 383 == htonl(0x20010010)); 384 384 } 385 385 386 + static inline void ipv6_addr_set_v4mapped(const __be32 addr, 387 + struct in6_addr *v4mapped) 388 + { 389 + ipv6_addr_set(v4mapped, 390 + 0, 0, 391 + htonl(0x0000FFFF), 392 + addr); 393 + } 394 + 386 395 /* 387 396 * find the first different bit between two addresses 388 397 * length of address must be a multiple of 32bits
+80 -38
net/sunrpc/svcauth_unix.c
··· 11 11 #include <linux/hash.h> 12 12 #include <linux/string.h> 13 13 #include <net/sock.h> 14 - 14 + #include <net/ipv6.h> 15 + #include <linux/kernel.h> 15 16 #define RPCDBG_FACILITY RPCDBG_AUTH 16 17 17 18 ··· 86 85 struct ip_map { 87 86 struct cache_head h; 88 87 char m_class[8]; /* e.g. "nfsd" */ 89 - struct in_addr m_addr; 88 + struct in6_addr m_addr; 90 89 struct unix_domain *m_client; 91 90 int m_add_change; 92 91 }; ··· 114 113 return (hash ^ (hash>>8)) & 0xff; 115 114 } 116 115 #endif 116 + static inline int hash_ip6(struct in6_addr ip) 117 + { 118 + return (hash_ip(ip.s6_addr32[0]) ^ 119 + hash_ip(ip.s6_addr32[1]) ^ 120 + hash_ip(ip.s6_addr32[2]) ^ 121 + hash_ip(ip.s6_addr32[3])); 122 + } 117 123 static int ip_map_match(struct cache_head *corig, struct cache_head *cnew) 118 124 { 119 125 struct ip_map *orig = container_of(corig, struct ip_map, h); 120 126 struct ip_map *new = container_of(cnew, struct ip_map, h); 121 127 return strcmp(orig->m_class, new->m_class) == 0 122 - && orig->m_addr.s_addr == new->m_addr.s_addr; 128 + && ipv6_addr_equal(&orig->m_addr, &new->m_addr); 123 129 } 124 130 static void ip_map_init(struct cache_head *cnew, struct cache_head *citem) 125 131 { ··· 134 126 struct ip_map *item = container_of(citem, struct ip_map, h); 135 127 136 128 strcpy(new->m_class, item->m_class); 137 - new->m_addr.s_addr = item->m_addr.s_addr; 129 + ipv6_addr_copy(&new->m_addr, &item->m_addr); 138 130 } 139 131 static void update(struct cache_head *cnew, struct cache_head *citem) 140 132 { ··· 158 150 struct cache_head *h, 159 151 char **bpp, int *blen) 160 152 { 161 - char text_addr[20]; 153 + char text_addr[40]; 162 154 struct ip_map *im = container_of(h, struct ip_map, h); 163 - __be32 addr = im->m_addr.s_addr; 164 155 165 - snprintf(text_addr, 20, "%u.%u.%u.%u", 166 - ntohl(addr) >> 24 & 0xff, 167 - ntohl(addr) >> 16 & 0xff, 168 - ntohl(addr) >> 8 & 0xff, 169 - ntohl(addr) >> 0 & 0xff); 170 - 156 + if (ipv6_addr_v4mapped(&(im->m_addr))) { 157 + snprintf(text_addr, 20, NIPQUAD_FMT, 158 + ntohl(im->m_addr.s6_addr32[3]) >> 24 & 0xff, 159 + ntohl(im->m_addr.s6_addr32[3]) >> 16 & 0xff, 160 + ntohl(im->m_addr.s6_addr32[3]) >> 8 & 0xff, 161 + ntohl(im->m_addr.s6_addr32[3]) >> 0 & 0xff); 162 + } else { 163 + snprintf(text_addr, 40, NIP6_FMT, NIP6(im->m_addr)); 164 + } 171 165 qword_add(bpp, blen, im->m_class); 172 166 qword_add(bpp, blen, text_addr); 173 167 (*bpp)[-1] = '\n'; 174 168 } 175 169 176 - static struct ip_map *ip_map_lookup(char *class, struct in_addr addr); 170 + static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr); 177 171 static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry); 178 172 179 173 static int ip_map_parse(struct cache_detail *cd, ··· 186 176 * for scratch: */ 187 177 char *buf = mesg; 188 178 int len; 189 - int b1,b2,b3,b4; 179 + int b1, b2, b3, b4, b5, b6, b7, b8; 190 180 char c; 191 181 char class[8]; 192 - struct in_addr addr; 182 + struct in6_addr addr; 193 183 int err; 194 184 195 185 struct ip_map *ipmp; ··· 208 198 len = qword_get(&mesg, buf, mlen); 209 199 if (len <= 0) return -EINVAL; 210 200 211 - if (sscanf(buf, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4) 201 + if (sscanf(buf, NIPQUAD_FMT "%c", &b1, &b2, &b3, &b4, &c) == 4) { 202 + addr.s6_addr32[0] = 0; 203 + addr.s6_addr32[1] = 0; 204 + addr.s6_addr32[2] = htonl(0xffff); 205 + addr.s6_addr32[3] = 206 + htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); 207 + } else if (sscanf(buf, NIP6_FMT "%c", 208 + &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &c) == 8) { 209 + addr.s6_addr16[0] = htons(b1); 210 + addr.s6_addr16[1] = htons(b2); 211 + addr.s6_addr16[2] = htons(b3); 212 + addr.s6_addr16[3] = htons(b4); 213 + addr.s6_addr16[4] = htons(b5); 214 + addr.s6_addr16[5] = htons(b6); 215 + addr.s6_addr16[6] = htons(b7); 216 + addr.s6_addr16[7] = htons(b8); 217 + } else 212 218 return -EINVAL; 213 219 214 220 expiry = get_expiry(&mesg); ··· 242 216 } else 243 217 dom = NULL; 244 218 245 - addr.s_addr = 246 - htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); 247 - 248 - ipmp = ip_map_lookup(class,addr); 219 + ipmp = ip_map_lookup(class, &addr); 249 220 if (ipmp) { 250 221 err = ip_map_update(ipmp, 251 222 container_of(dom, struct unix_domain, h), ··· 262 239 struct cache_head *h) 263 240 { 264 241 struct ip_map *im; 265 - struct in_addr addr; 242 + struct in6_addr addr; 266 243 char *dom = "-no-domain-"; 267 244 268 245 if (h == NULL) { ··· 271 248 } 272 249 im = container_of(h, struct ip_map, h); 273 250 /* class addr domain */ 274 - addr = im->m_addr; 251 + ipv6_addr_copy(&addr, &im->m_addr); 275 252 276 253 if (test_bit(CACHE_VALID, &h->flags) && 277 254 !test_bit(CACHE_NEGATIVE, &h->flags)) 278 255 dom = im->m_client->h.name; 279 256 280 - seq_printf(m, "%s %d.%d.%d.%d %s\n", 281 - im->m_class, 282 - ntohl(addr.s_addr) >> 24 & 0xff, 283 - ntohl(addr.s_addr) >> 16 & 0xff, 284 - ntohl(addr.s_addr) >> 8 & 0xff, 285 - ntohl(addr.s_addr) >> 0 & 0xff, 286 - dom 287 - ); 257 + if (ipv6_addr_v4mapped(&addr)) { 258 + seq_printf(m, "%s" NIPQUAD_FMT "%s\n", 259 + im->m_class, 260 + ntohl(addr.s6_addr32[3]) >> 24 & 0xff, 261 + ntohl(addr.s6_addr32[3]) >> 16 & 0xff, 262 + ntohl(addr.s6_addr32[3]) >> 8 & 0xff, 263 + ntohl(addr.s6_addr32[3]) >> 0 & 0xff, 264 + dom); 265 + } else { 266 + seq_printf(m, "%s" NIP6_FMT "%s\n", 267 + im->m_class, NIP6(addr), dom); 268 + } 288 269 return 0; 289 270 } 290 271 ··· 308 281 .alloc = ip_map_alloc, 309 282 }; 310 283 311 - static struct ip_map *ip_map_lookup(char *class, struct in_addr addr) 284 + static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr) 312 285 { 313 286 struct ip_map ip; 314 287 struct cache_head *ch; 315 288 316 289 strcpy(ip.m_class, class); 317 - ip.m_addr = addr; 290 + ipv6_addr_copy(&ip.m_addr, addr); 318 291 ch = sunrpc_cache_lookup(&ip_map_cache, &ip.h, 319 292 hash_str(class, IP_HASHBITS) ^ 320 - hash_ip(addr.s_addr)); 293 + hash_ip6(*addr)); 321 294 322 295 if (ch) 323 296 return container_of(ch, struct ip_map, h); ··· 346 319 ch = sunrpc_cache_update(&ip_map_cache, 347 320 &ip.h, &ipm->h, 348 321 hash_str(ipm->m_class, IP_HASHBITS) ^ 349 - hash_ip(ipm->m_addr.s_addr)); 322 + hash_ip6(ipm->m_addr)); 350 323 if (!ch) 351 324 return -ENOMEM; 352 325 cache_put(ch, &ip_map_cache); 353 326 return 0; 354 327 } 355 328 356 - int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom) 329 + int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom) 357 330 { 358 331 struct unix_domain *udom; 359 332 struct ip_map *ipmp; ··· 382 355 } 383 356 EXPORT_SYMBOL(auth_unix_forget_old); 384 357 385 - struct auth_domain *auth_unix_lookup(struct in_addr addr) 358 + struct auth_domain *auth_unix_lookup(struct in6_addr *addr) 386 359 { 387 360 struct ip_map *ipm; 388 361 struct auth_domain *rv; ··· 677 650 int 678 651 svcauth_unix_set_client(struct svc_rqst *rqstp) 679 652 { 680 - struct sockaddr_in *sin = svc_addr_in(rqstp); 653 + struct sockaddr_in *sin; 654 + struct sockaddr_in6 *sin6, sin6_storage; 681 655 struct ip_map *ipm; 656 + 657 + switch (rqstp->rq_addr.ss_family) { 658 + case AF_INET: 659 + sin = svc_addr_in(rqstp); 660 + sin6 = &sin6_storage; 661 + ipv6_addr_set(&sin6->sin6_addr, 0, 0, 662 + htonl(0x0000FFFF), sin->sin_addr.s_addr); 663 + break; 664 + case AF_INET6: 665 + sin6 = svc_addr_in6(rqstp); 666 + break; 667 + default: 668 + BUG(); 669 + } 682 670 683 671 rqstp->rq_client = NULL; 684 672 if (rqstp->rq_proc == 0) ··· 702 660 ipm = ip_map_cached_get(rqstp); 703 661 if (ipm == NULL) 704 662 ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, 705 - sin->sin_addr); 663 + &sin6->sin6_addr); 706 664 707 665 if (ipm == NULL) 708 666 return SVC_DENIED;