linux/fs/nfs/dns_resolve.c
<<
>>
Prefs
   1/*
   2 * linux/fs/nfs/dns_resolve.c
   3 *
   4 * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
   5 *
   6 * Resolves DNS hostnames into valid ip addresses
   7 */
   8
   9#ifdef CONFIG_NFS_USE_KERNEL_DNS
  10
  11#include <linux/module.h>
  12#include <linux/sunrpc/clnt.h>
  13#include <linux/dns_resolver.h>
  14#include "dns_resolve.h"
  15
  16ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen,
  17                struct sockaddr *sa, size_t salen)
  18{
  19        ssize_t ret;
  20        char *ip_addr = NULL;
  21        int ip_len;
  22
  23        ip_len = dns_query(NULL, name, namelen, NULL, &ip_addr, NULL);
  24        if (ip_len > 0)
  25                ret = rpc_pton(net, ip_addr, ip_len, sa, salen);
  26        else
  27                ret = -ESRCH;
  28        kfree(ip_addr);
  29        return ret;
  30}
  31EXPORT_SYMBOL_GPL(nfs_dns_resolve_name);
  32
  33#else
  34
  35#include <linux/module.h>
  36#include <linux/hash.h>
  37#include <linux/string.h>
  38#include <linux/kmod.h>
  39#include <linux/slab.h>
  40#include <linux/module.h>
  41#include <linux/socket.h>
  42#include <linux/seq_file.h>
  43#include <linux/inet.h>
  44#include <linux/sunrpc/clnt.h>
  45#include <linux/sunrpc/cache.h>
  46#include <linux/sunrpc/svcauth.h>
  47#include <linux/sunrpc/rpc_pipe_fs.h>
  48
  49#include "dns_resolve.h"
  50#include "cache_lib.h"
  51#include "netns.h"
  52
  53#define NFS_DNS_HASHBITS 4
  54#define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS)
  55
  56struct nfs_dns_ent {
  57        struct cache_head h;
  58
  59        char *hostname;
  60        size_t namelen;
  61
  62        struct sockaddr_storage addr;
  63        size_t addrlen;
  64};
  65
  66
  67static void nfs_dns_ent_update(struct cache_head *cnew,
  68                struct cache_head *ckey)
  69{
  70        struct nfs_dns_ent *new;
  71        struct nfs_dns_ent *key;
  72
  73        new = container_of(cnew, struct nfs_dns_ent, h);
  74        key = container_of(ckey, struct nfs_dns_ent, h);
  75
  76        memcpy(&new->addr, &key->addr, key->addrlen);
  77        new->addrlen = key->addrlen;
  78}
  79
  80static void nfs_dns_ent_init(struct cache_head *cnew,
  81                struct cache_head *ckey)
  82{
  83        struct nfs_dns_ent *new;
  84        struct nfs_dns_ent *key;
  85
  86        new = container_of(cnew, struct nfs_dns_ent, h);
  87        key = container_of(ckey, struct nfs_dns_ent, h);
  88
  89        kfree(new->hostname);
  90        new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL);
  91        if (new->hostname) {
  92                new->namelen = key->namelen;
  93                nfs_dns_ent_update(cnew, ckey);
  94        } else {
  95                new->namelen = 0;
  96                new->addrlen = 0;
  97        }
  98}
  99
 100static void nfs_dns_ent_put(struct kref *ref)
 101{
 102        struct nfs_dns_ent *item;
 103
 104        item = container_of(ref, struct nfs_dns_ent, h.ref);
 105        kfree(item->hostname);
 106        kfree(item);
 107}
 108
 109static struct cache_head *nfs_dns_ent_alloc(void)
 110{
 111        struct nfs_dns_ent *item = kmalloc(sizeof(*item), GFP_KERNEL);
 112
 113        if (item != NULL) {
 114                item->hostname = NULL;
 115                item->namelen = 0;
 116                item->addrlen = 0;
 117                return &item->h;
 118        }
 119        return NULL;
 120};
 121
 122static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key)
 123{
 124        return hash_str(key->hostname, NFS_DNS_HASHBITS);
 125}
 126
 127static void nfs_dns_request(struct cache_detail *cd,
 128                struct cache_head *ch,
 129                char **bpp, int *blen)
 130{
 131        struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
 132
 133        qword_add(bpp, blen, key->hostname);
 134        (*bpp)[-1] = '\n';
 135}
 136
 137static int nfs_dns_upcall(struct cache_detail *cd,
 138                struct cache_head *ch)
 139{
 140        struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
 141        int ret;
 142
 143        ret = nfs_cache_upcall(cd, key->hostname);
 144        if (ret)
 145                ret = sunrpc_cache_pipe_upcall(cd, ch, nfs_dns_request);
 146        return ret;
 147}
 148
 149static int nfs_dns_match(struct cache_head *ca,
 150                struct cache_head *cb)
 151{
 152        struct nfs_dns_ent *a;
 153        struct nfs_dns_ent *b;
 154
 155        a = container_of(ca, struct nfs_dns_ent, h);
 156        b = container_of(cb, struct nfs_dns_ent, h);
 157
 158        if (a->namelen == 0 || a->namelen != b->namelen)
 159                return 0;
 160        return memcmp(a->hostname, b->hostname, a->namelen) == 0;
 161}
 162
 163static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd,
 164                struct cache_head *h)
 165{
 166        struct nfs_dns_ent *item;
 167        long ttl;
 168
 169        if (h == NULL) {
 170                seq_puts(m, "# ip address      hostname        ttl\n");
 171                return 0;
 172        }
 173        item = container_of(h, struct nfs_dns_ent, h);
 174        ttl = item->h.expiry_time - seconds_since_boot();
 175        if (ttl < 0)
 176                ttl = 0;
 177
 178        if (!test_bit(CACHE_NEGATIVE, &h->flags)) {
 179                char buf[INET6_ADDRSTRLEN+IPV6_SCOPE_ID_LEN+1];
 180
 181                rpc_ntop((struct sockaddr *)&item->addr, buf, sizeof(buf));
 182                seq_printf(m, "%15s ", buf);
 183        } else
 184                seq_puts(m, "<none>          ");
 185        seq_printf(m, "%15s %ld\n", item->hostname, ttl);
 186        return 0;
 187}
 188
 189static struct nfs_dns_ent *nfs_dns_lookup(struct cache_detail *cd,
 190                struct nfs_dns_ent *key)
 191{
 192        struct cache_head *ch;
 193
 194        ch = sunrpc_cache_lookup(cd,
 195                        &key->h,
 196                        nfs_dns_hash(key));
 197        if (!ch)
 198                return NULL;
 199        return container_of(ch, struct nfs_dns_ent, h);
 200}
 201
 202static struct nfs_dns_ent *nfs_dns_update(struct cache_detail *cd,
 203                struct nfs_dns_ent *new,
 204                struct nfs_dns_ent *key)
 205{
 206        struct cache_head *ch;
 207
 208        ch = sunrpc_cache_update(cd,
 209                        &new->h, &key->h,
 210                        nfs_dns_hash(key));
 211        if (!ch)
 212                return NULL;
 213        return container_of(ch, struct nfs_dns_ent, h);
 214}
 215
 216static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
 217{
 218        char buf1[NFS_DNS_HOSTNAME_MAXLEN+1];
 219        struct nfs_dns_ent key, *item;
 220        unsigned int ttl;
 221        ssize_t len;
 222        int ret = -EINVAL;
 223
 224        if (buf[buflen-1] != '\n')
 225                goto out;
 226        buf[buflen-1] = '\0';
 227
 228        len = qword_get(&buf, buf1, sizeof(buf1));
 229        if (len <= 0)
 230                goto out;
 231        key.addrlen = rpc_pton(cd->net, buf1, len,
 232                        (struct sockaddr *)&key.addr,
 233                        sizeof(key.addr));
 234
 235        len = qword_get(&buf, buf1, sizeof(buf1));
 236        if (len <= 0)
 237                goto out;
 238
 239        key.hostname = buf1;
 240        key.namelen = len;
 241        memset(&key.h, 0, sizeof(key.h));
 242
 243        if (get_uint(&buf, &ttl) < 0)
 244                goto out;
 245        if (ttl == 0)
 246                goto out;
 247        key.h.expiry_time = ttl + seconds_since_boot();
 248
 249        ret = -ENOMEM;
 250        item = nfs_dns_lookup(cd, &key);
 251        if (item == NULL)
 252                goto out;
 253
 254        if (key.addrlen == 0)
 255                set_bit(CACHE_NEGATIVE, &key.h.flags);
 256
 257        item = nfs_dns_update(cd, &key, item);
 258        if (item == NULL)
 259                goto out;
 260
 261        ret = 0;
 262        cache_put(&item->h, cd);
 263out:
 264        return ret;
 265}
 266
 267static int do_cache_lookup(struct cache_detail *cd,
 268                struct nfs_dns_ent *key,
 269                struct nfs_dns_ent **item,
 270                struct nfs_cache_defer_req *dreq)
 271{
 272        int ret = -ENOMEM;
 273
 274        *item = nfs_dns_lookup(cd, key);
 275        if (*item) {
 276                ret = cache_check(cd, &(*item)->h, &dreq->req);
 277                if (ret)
 278                        *item = NULL;
 279        }
 280        return ret;
 281}
 282
 283static int do_cache_lookup_nowait(struct cache_detail *cd,
 284                struct nfs_dns_ent *key,
 285                struct nfs_dns_ent **item)
 286{
 287        int ret = -ENOMEM;
 288
 289        *item = nfs_dns_lookup(cd, key);
 290        if (!*item)
 291                goto out_err;
 292        ret = -ETIMEDOUT;
 293        if (!test_bit(CACHE_VALID, &(*item)->h.flags)
 294                        || (*item)->h.expiry_time < seconds_since_boot()
 295                        || cd->flush_time > (*item)->h.last_refresh)
 296                goto out_put;
 297        ret = -ENOENT;
 298        if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
 299                goto out_put;
 300        return 0;
 301out_put:
 302        cache_put(&(*item)->h, cd);
 303out_err:
 304        *item = NULL;
 305        return ret;
 306}
 307
 308static int do_cache_lookup_wait(struct cache_detail *cd,
 309                struct nfs_dns_ent *key,
 310                struct nfs_dns_ent **item)
 311{
 312        struct nfs_cache_defer_req *dreq;
 313        int ret = -ENOMEM;
 314
 315        dreq = nfs_cache_defer_req_alloc();
 316        if (!dreq)
 317                goto out;
 318        ret = do_cache_lookup(cd, key, item, dreq);
 319        if (ret == -EAGAIN) {
 320                ret = nfs_cache_wait_for_upcall(dreq);
 321                if (!ret)
 322                        ret = do_cache_lookup_nowait(cd, key, item);
 323        }
 324        nfs_cache_defer_req_put(dreq);
 325out:
 326        return ret;
 327}
 328
 329ssize_t nfs_dns_resolve_name(struct net *net, char *name,
 330                size_t namelen, struct sockaddr *sa, size_t salen)
 331{
 332        struct nfs_dns_ent key = {
 333                .hostname = name,
 334                .namelen = namelen,
 335        };
 336        struct nfs_dns_ent *item = NULL;
 337        ssize_t ret;
 338        struct nfs_net *nn = net_generic(net, nfs_net_id);
 339
 340        ret = do_cache_lookup_wait(nn->nfs_dns_resolve, &key, &item);
 341        if (ret == 0) {
 342                if (salen >= item->addrlen) {
 343                        memcpy(sa, &item->addr, item->addrlen);
 344                        ret = item->addrlen;
 345                } else
 346                        ret = -EOVERFLOW;
 347                cache_put(&item->h, nn->nfs_dns_resolve);
 348        } else if (ret == -ENOENT)
 349                ret = -ESRCH;
 350        return ret;
 351}
 352EXPORT_SYMBOL_GPL(nfs_dns_resolve_name);
 353
 354int nfs_dns_resolver_cache_init(struct net *net)
 355{
 356        int err = -ENOMEM;
 357        struct nfs_net *nn = net_generic(net, nfs_net_id);
 358        struct cache_detail *cd;
 359        struct cache_head **tbl;
 360
 361        cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL);
 362        if (cd == NULL)
 363                goto err_cd;
 364
 365        tbl = kzalloc(NFS_DNS_HASHTBL_SIZE * sizeof(struct cache_head *),
 366                        GFP_KERNEL);
 367        if (tbl == NULL)
 368                goto err_tbl;
 369
 370        cd->owner = THIS_MODULE,
 371        cd->hash_size = NFS_DNS_HASHTBL_SIZE,
 372        cd->hash_table = tbl,
 373        cd->name = "dns_resolve",
 374        cd->cache_put = nfs_dns_ent_put,
 375        cd->cache_upcall = nfs_dns_upcall,
 376        cd->cache_parse = nfs_dns_parse,
 377        cd->cache_show = nfs_dns_show,
 378        cd->match = nfs_dns_match,
 379        cd->init = nfs_dns_ent_init,
 380        cd->update = nfs_dns_ent_update,
 381        cd->alloc = nfs_dns_ent_alloc,
 382
 383        nfs_cache_init(cd);
 384        err = nfs_cache_register_net(net, cd);
 385        if (err)
 386                goto err_reg;
 387        nn->nfs_dns_resolve = cd;
 388        return 0;
 389
 390err_reg:
 391        nfs_cache_destroy(cd);
 392        kfree(cd->hash_table);
 393err_tbl:
 394        kfree(cd);
 395err_cd:
 396        return err;
 397}
 398
 399void nfs_dns_resolver_cache_destroy(struct net *net)
 400{
 401        struct nfs_net *nn = net_generic(net, nfs_net_id);
 402        struct cache_detail *cd = nn->nfs_dns_resolve;
 403
 404        nfs_cache_unregister_net(net, cd);
 405        nfs_cache_destroy(cd);
 406        kfree(cd->hash_table);
 407        kfree(cd);
 408}
 409
 410static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
 411                           void *ptr)
 412{
 413        struct super_block *sb = ptr;
 414        struct net *net = sb->s_fs_info;
 415        struct nfs_net *nn = net_generic(net, nfs_net_id);
 416        struct cache_detail *cd = nn->nfs_dns_resolve;
 417        int ret = 0;
 418
 419        if (cd == NULL)
 420                return 0;
 421
 422        if (!try_module_get(THIS_MODULE))
 423                return 0;
 424
 425        switch (event) {
 426        case RPC_PIPEFS_MOUNT:
 427                ret = nfs_cache_register_sb(sb, cd);
 428                break;
 429        case RPC_PIPEFS_UMOUNT:
 430                nfs_cache_unregister_sb(sb, cd);
 431                break;
 432        default:
 433                ret = -ENOTSUPP;
 434                break;
 435        }
 436        module_put(THIS_MODULE);
 437        return ret;
 438}
 439
 440static struct notifier_block nfs_dns_resolver_block = {
 441        .notifier_call  = rpc_pipefs_event,
 442};
 443
 444int nfs_dns_resolver_init(void)
 445{
 446        return rpc_pipefs_notifier_register(&nfs_dns_resolver_block);
 447}
 448
 449void nfs_dns_resolver_destroy(void)
 450{
 451        rpc_pipefs_notifier_unregister(&nfs_dns_resolver_block);
 452}
 453#endif
 454
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.