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 long 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        ttl = get_expiry(&buf);
 244        if (ttl == 0)
 245                goto out;
 246        key.h.expiry_time = ttl + seconds_since_boot();
 247
 248        ret = -ENOMEM;
 249        item = nfs_dns_lookup(cd, &key);
 250        if (item == NULL)
 251                goto out;
 252
 253        if (key.addrlen == 0)
 254                set_bit(CACHE_NEGATIVE, &key.h.flags);
 255
 256        item = nfs_dns_update(cd, &key, item);
 257        if (item == NULL)
 258                goto out;
 259
 260        ret = 0;
 261        cache_put(&item->h, cd);
 262out:
 263        return ret;
 264}
 265
 266static int do_cache_lookup(struct cache_detail *cd,
 267                struct nfs_dns_ent *key,
 268                struct nfs_dns_ent **item,
 269                struct nfs_cache_defer_req *dreq)
 270{
 271        int ret = -ENOMEM;
 272
 273        *item = nfs_dns_lookup(cd, key);
 274        if (*item) {
 275                ret = cache_check(cd, &(*item)->h, &dreq->req);
 276                if (ret)
 277                        *item = NULL;
 278        }
 279        return ret;
 280}
 281
 282static int do_cache_lookup_nowait(struct cache_detail *cd,
 283                struct nfs_dns_ent *key,
 284                struct nfs_dns_ent **item)
 285{
 286        int ret = -ENOMEM;
 287
 288        *item = nfs_dns_lookup(cd, key);
 289        if (!*item)
 290                goto out_err;
 291        ret = -ETIMEDOUT;
 292        if (!test_bit(CACHE_VALID, &(*item)->h.flags)
 293                        || (*item)->h.expiry_time < seconds_since_boot()
 294                        || cd->flush_time > (*item)->h.last_refresh)
 295                goto out_put;
 296        ret = -ENOENT;
 297        if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
 298                goto out_put;
 299        return 0;
 300out_put:
 301        cache_put(&(*item)->h, cd);
 302out_err:
 303        *item = NULL;
 304        return ret;
 305}
 306
 307static int do_cache_lookup_wait(struct cache_detail *cd,
 308                struct nfs_dns_ent *key,
 309                struct nfs_dns_ent **item)
 310{
 311        struct nfs_cache_defer_req *dreq;
 312        int ret = -ENOMEM;
 313
 314        dreq = nfs_cache_defer_req_alloc();
 315        if (!dreq)
 316                goto out;
 317        ret = do_cache_lookup(cd, key, item, dreq);
 318        if (ret == -EAGAIN) {
 319                ret = nfs_cache_wait_for_upcall(dreq);
 320                if (!ret)
 321                        ret = do_cache_lookup_nowait(cd, key, item);
 322        }
 323        nfs_cache_defer_req_put(dreq);
 324out:
 325        return ret;
 326}
 327
 328ssize_t nfs_dns_resolve_name(struct net *net, char *name,
 329                size_t namelen, struct sockaddr *sa, size_t salen)
 330{
 331        struct nfs_dns_ent key = {
 332                .hostname = name,
 333                .namelen = namelen,
 334        };
 335        struct nfs_dns_ent *item = NULL;
 336        ssize_t ret;
 337        struct nfs_net *nn = net_generic(net, nfs_net_id);
 338
 339        ret = do_cache_lookup_wait(nn->nfs_dns_resolve, &key, &item);
 340        if (ret == 0) {
 341                if (salen >= item->addrlen) {
 342                        memcpy(sa, &item->addr, item->addrlen);
 343                        ret = item->addrlen;
 344                } else
 345                        ret = -EOVERFLOW;
 346                cache_put(&item->h, nn->nfs_dns_resolve);
 347        } else if (ret == -ENOENT)
 348                ret = -ESRCH;
 349        return ret;
 350}
 351EXPORT_SYMBOL_GPL(nfs_dns_resolve_name);
 352
 353int nfs_dns_resolver_cache_init(struct net *net)
 354{
 355        int err = -ENOMEM;
 356        struct nfs_net *nn = net_generic(net, nfs_net_id);
 357        struct cache_detail *cd;
 358        struct cache_head **tbl;
 359
 360        cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL);
 361        if (cd == NULL)
 362                goto err_cd;
 363
 364        tbl = kzalloc(NFS_DNS_HASHTBL_SIZE * sizeof(struct cache_head *),
 365                        GFP_KERNEL);
 366        if (tbl == NULL)
 367                goto err_tbl;
 368
 369        cd->owner = THIS_MODULE,
 370        cd->hash_size = NFS_DNS_HASHTBL_SIZE,
 371        cd->hash_table = tbl,
 372        cd->name = "dns_resolve",
 373        cd->cache_put = nfs_dns_ent_put,
 374        cd->cache_upcall = nfs_dns_upcall,
 375        cd->cache_parse = nfs_dns_parse,
 376        cd->cache_show = nfs_dns_show,
 377        cd->match = nfs_dns_match,
 378        cd->init = nfs_dns_ent_init,
 379        cd->update = nfs_dns_ent_update,
 380        cd->alloc = nfs_dns_ent_alloc,
 381
 382        nfs_cache_init(cd);
 383        err = nfs_cache_register_net(net, cd);
 384        if (err)
 385                goto err_reg;
 386        nn->nfs_dns_resolve = cd;
 387        return 0;
 388
 389err_reg:
 390        nfs_cache_destroy(cd);
 391        kfree(cd->hash_table);
 392err_tbl:
 393        kfree(cd);
 394err_cd:
 395        return err;
 396}
 397
 398void nfs_dns_resolver_cache_destroy(struct net *net)
 399{
 400        struct nfs_net *nn = net_generic(net, nfs_net_id);
 401        struct cache_detail *cd = nn->nfs_dns_resolve;
 402
 403        nfs_cache_unregister_net(net, cd);
 404        nfs_cache_destroy(cd);
 405        kfree(cd->hash_table);
 406        kfree(cd);
 407}
 408
 409static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
 410                           void *ptr)
 411{
 412        struct super_block *sb = ptr;
 413        struct net *net = sb->s_fs_info;
 414        struct nfs_net *nn = net_generic(net, nfs_net_id);
 415        struct cache_detail *cd = nn->nfs_dns_resolve;
 416        int ret = 0;
 417
 418        if (cd == NULL)
 419                return 0;
 420
 421        if (!try_module_get(THIS_MODULE))
 422                return 0;
 423
 424        switch (event) {
 425        case RPC_PIPEFS_MOUNT:
 426                ret = nfs_cache_register_sb(sb, cd);
 427                break;
 428        case RPC_PIPEFS_UMOUNT:
 429                nfs_cache_unregister_sb(sb, cd);
 430                break;
 431        default:
 432                ret = -ENOTSUPP;
 433                break;
 434        }
 435        module_put(THIS_MODULE);
 436        return ret;
 437}
 438
 439static struct notifier_block nfs_dns_resolver_block = {
 440        .notifier_call  = rpc_pipefs_event,
 441};
 442
 443int nfs_dns_resolver_init(void)
 444{
 445        return rpc_pipefs_notifier_register(&nfs_dns_resolver_block);
 446}
 447
 448void nfs_dns_resolver_destroy(void)
 449{
 450        rpc_pipefs_notifier_unregister(&nfs_dns_resolver_block);
 451}
 452#endif
 453
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.